<commit_before>package message

import (
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"socialapi/config"
	"socialapi/models"
	"socialapi/request"
	"socialapi/workers/api/realtimehelper"
	"socialapi/workers/common/response"
	"time"

	"github.com/koding/bongo"
)

func Create(u *url.URL, h http.Header, req *models.ChannelMessage) (int, http.Header, interface{}, error) {
	channelId, err := request.GetURIInt64(u, "id")
	if err != nil {
		return response.NewBadRequest(err)
	}

	// override message type
	// all of the messages coming from client-side
	// should be marked as POST
	req.TypeConstant = models.ChannelMessage_TYPE_POST

	// set initial channel id
	req.InitialChannelId = channelId

	if err := checkThrottle(channelId, req.AccountId); err != nil {
		return response.NewBadRequest(err)
	}

	if err := req.Create(); err != nil {
		// todo this should be internal server error
		return response.NewBadRequest(err)
	}

	go func() {
		// send it to pubnub
		if err := realtimehelper.SubscribeMessage(req); err != nil {
			fmt.Printf("Could not subscribe to message: %s \n", err)
		}
	}()

	cml := models.NewChannelMessageList()
	// override channel id
	cml.ChannelId = channelId
	cml.MessageId = req.Id
	if err := cml.Create(); err != nil {
		// todo this should be internal server error
		return response.NewBadRequest(err)
	}

	cmc := models.NewChannelMessageContainer()
	err = cmc.Fetch(req.Id, request.GetQuery(u))

	// assign client request id back to message response because
	// client uses it for latency compansation
	cmc.Message.ClientRequestId = req.ClientRequestId
	return response.HandleResultAndError(cmc, err)
}

func checkThrottle(channelId, requesterId int64) error {
	c, err := models.ChannelById(channelId)
	if err != nil {
		return err
	}

	if c.TypeConstant != models.Channel_TYPE_GROUP {
		return nil
	}

	cm := models.NewChannelMessage()

	conf := config.MustGet()

	// if oit is defaul treturn  early
	if conf.Limits.PostThrottleDuration == "" {
		return nil
	}

	// if throttle count is zero, it meands it is not set
	if conf.Limits.PostThrottleCount == 0 {
		return nil
	}

	dur, err := time.ParseDuration(conf.Limits.PostThrottleDuration)
	if err != nil {
		return err
	}

	// subtrack duration from current time
	prevTime := time.Now().UTC().Truncate(dur)

	// count sends positional parameters, no need to sanitize input
	count, err := bongo.B.Count(
		cm,
		"initial_channel_id = ? and "+
			"account_id = ? and "+
			"created_at > ?",
		channelId,
		requesterId,
		prevTime.Format(time.RFC3339Nano),
	)
	if err != nil {
		return err
	}

	if count > conf.Limits.PostThrottleCount {
		return fmt.Errorf("reached to throttle, current post count %d for user %d", count, requesterId)
	}

	return nil
}

func Delete(u *url.URL, h http.Header, req *models.ChannelMessage) (int, http.Header, interface{}, error) {

	id, err := request.GetURIInt64(u, "id")
	if err != nil {
		return response.NewBadRequest(err)
	}

	if err := req.ById(id); err != nil {
		if err == bongo.RecordNotFound {
			return response.NewNotFound()
		}
		return response.NewBadRequest(err)
	}

	// if this is a reply no need to delete it's replies
	if req.TypeConstant == models.ChannelMessage_TYPE_REPLY {
		mr := models.NewMessageReply()
		mr.ReplyId = id
		parent, err := mr.FetchParent()
		if err != nil {
			return response.NewBadRequest(err)
		}

		// delete the message here
		err = req.DeleteMessageAndDependencies(false)
		// then invalidate the cache of the parent message
		bongo.B.AddToCache(parent)

	} else {
		err = req.DeleteMessageAndDependencies(true)
	}

	if err != nil {
		return response.NewBadRequest(err)
	}

	// yes it is deleted but not removed completely from our system
	return response.NewDeleted()
}

func Update(u *url.URL, h http.Header, req *models.ChannelMessage) (int, http.Header, interface{}, error) {
	id, err := request.GetURIInt64(u, "id")
	if err != nil {
		return response.NewBadRequest(err)
	}

	body := req.Body
	if err := req.ById(id); err != nil {
		if err == bongo.RecordNotFound {
			return response.NewNotFound()
		}
		return response.NewBadRequest(err)
	}

	if req.Id == 0 {
		return response.NewBadRequest(err)
	}

	req.Body = body
	if err := req.Update(); err != nil {
		return response.NewBadRequest(err)
	}

	cmc := models.NewChannelMessageContainer()
	return response.HandleResultAndError(cmc, cmc.Fetch(id, request.GetQuery(u)))
}

func Get(u *url.URL, h http.Header, _ interface{}) (int, http.Header, interface{}, error) {
	cm, err := getMessageByUrl(u)
	if err != nil {
		return response.NewBadRequest(err)
	}

	if cm.Id == 0 {
		return response.NewNotFound()
	}

	cmc := models.NewChannelMessageContainer()
	return response.HandleResultAndError(cmc, cmc.Fetch(cm.Id, request.GetQuery(u)))
}

func getMessageByUrl(u *url.URL) (*models.ChannelMessage, error) {

	// TODO
	// fmt.Println(`
	// 	------->
	//             ADD SECURTY CHECK FOR VISIBILTY OF THE MESSAGE
	//                         FOR THE REQUESTER
	//     ------->"`,
	// )

	id, err := request.GetURIInt64(u, "id")
	if err != nil {
		return nil, err
	}

	// get url query params
	q := request.GetQuery(u)

	query := &bongo.Query{
		Selector: map[string]interface{}{
			"id": id,
		},
		Pagination: *bongo.NewPagination(1, 0),
	}

	cm := models.NewChannelMessage()
	// add exempt info
	query.AddScope(models.RemoveTrollContent(cm, q.ShowExempt))

	if err := cm.One(query); err != nil {
		return nil, err
	}

	return cm, nil
}

func GetWithRelated(u *url.URL, h http.Header, _ interface{}) (int, http.Header, interface{}, error) {
	cm, err := getMessageByUrl(u)
	if err != nil {
		return response.NewBadRequest(err)
	}

	if cm.Id == 0 {
		return response.NewNotFound()
	}

	q := request.GetQuery(u)

	cmc := models.NewChannelMessageContainer()
	if err := cmc.Fetch(cm.Id, q); err != nil {
		return response.NewBadRequest(err)
	}

	cmc.AddIsInteracted(q).AddIsFollowed(q)

	return response.HandleResultAndError(cmc, cmc.Err)
}

func GetBySlug(u *url.URL, h http.Header, _ interface{}) (int, http.Header, interface{}, error) {
	q := request.GetQuery(u)

	if q.Slug == "" {
		return response.NewBadRequest(errors.New("slug is not set"))
	}

	cm := models.NewChannelMessage()
	if err := cm.BySlug(q); err != nil {
		if err == bongo.RecordNotFound {
			return response.NewNotFound()
		}
		return response.NewBadRequest(err)
	}

	cmc := models.NewChannelMessageContainer()
	if err := cmc.Fetch(cm.Id, q); err != nil {
		return response.NewBadRequest(err)
	}

	cmc.AddIsInteracted(q).AddIsFollowed(q)

	return response.HandleResultAndError(cmc, cmc.Err)
}
<commit_msg>social: remove message pubnub subscription from message.Create<commit_after>package message

import (
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"socialapi/config"
	"socialapi/models"
	"socialapi/request"
	"socialapi/workers/common/response"
	"time"

	"github.com/koding/bongo"
)

func Create(u *url.URL, h http.Header, req *models.ChannelMessage) (int, http.Header, interface{}, error) {
	channelId, err := request.GetURIInt64(u, "id")
	if err != nil {
		return response.NewBadRequest(err)
	}

	// override message type
	// all of the messages coming from client-side
	// should be marked as POST
	req.TypeConstant = models.ChannelMessage_TYPE_POST

	// set initial channel id
	req.InitialChannelId = channelId

	if err := checkThrottle(channelId, req.AccountId); err != nil {
		return response.NewBadRequest(err)
	}

	if err := req.Create(); err != nil {
		// todo this should be internal server error
		return response.NewBadRequest(err)
	}

	cml := models.NewChannelMessageList()
	// override channel id
	cml.ChannelId = channelId
	cml.MessageId = req.Id
	if err := cml.Create(); err != nil {
		// todo this should be internal server error
		return response.NewBadRequest(err)
	}

	cmc := models.NewChannelMessageContainer()
	err = cmc.Fetch(req.Id, request.GetQuery(u))

	// assign client request id back to message response because
	// client uses it for latency compansation
	cmc.Message.ClientRequestId = req.ClientRequestId
	return response.HandleResultAndError(cmc, err)
}

func checkThrottle(channelId, requesterId int64) error {
	c, err := models.ChannelById(channelId)
	if err != nil {
		return err
	}

	if c.TypeConstant != models.Channel_TYPE_GROUP {
		return nil
	}

	cm := models.NewChannelMessage()

	conf := config.MustGet()

	// if oit is defaul treturn  early
	if conf.Limits.PostThrottleDuration == "" {
		return nil
	}

	// if throttle count is zero, it meands it is not set
	if conf.Limits.PostThrottleCount == 0 {
		return nil
	}

	dur, err := time.ParseDuration(conf.Limits.PostThrottleDuration)
	if err != nil {
		return err
	}

	// subtrack duration from current time
	prevTime := time.Now().UTC().Truncate(dur)

	// count sends positional parameters, no need to sanitize input
	count, err := bongo.B.Count(
		cm,
		"initial_channel_id = ? and "+
			"account_id = ? and "+
			"created_at > ?",
		channelId,
		requesterId,
		prevTime.Format(time.RFC3339Nano),
	)
	if err != nil {
		return err
	}

	if count > conf.Limits.PostThrottleCount {
		return fmt.Errorf("reached to throttle, current post count %d for user %d", count, requesterId)
	}

	return nil
}

func Delete(u *url.URL, h http.Header, req *models.ChannelMessage) (int, http.Header, interface{}, error) {

	id, err := request.GetURIInt64(u, "id")
	if err != nil {
		return response.NewBadRequest(err)
	}

	if err := req.ById(id); err != nil {
		if err == bongo.RecordNotFound {
			return response.NewNotFound()
		}
		return response.NewBadRequest(err)
	}

	// if this is a reply no need to delete it's replies
	if req.TypeConstant == models.ChannelMessage_TYPE_REPLY {
		mr := models.NewMessageReply()
		mr.ReplyId = id
		parent, err := mr.FetchParent()
		if err != nil {
			return response.NewBadRequest(err)
		}

		// delete the message here
		err = req.DeleteMessageAndDependencies(false)
		// then invalidate the cache of the parent message
		bongo.B.AddToCache(parent)

	} else {
		err = req.DeleteMessageAndDependencies(true)
	}

	if err != nil {
		return response.NewBadRequest(err)
	}

	// yes it is deleted but not removed completely from our system
	return response.NewDeleted()
}

func Update(u *url.URL, h http.Header, req *models.ChannelMessage) (int, http.Header, interface{}, error) {
	id, err := request.GetURIInt64(u, "id")
	if err != nil {
		return response.NewBadRequest(err)
	}

	body := req.Body
	if err := req.ById(id); err != nil {
		if err == bongo.RecordNotFound {
			return response.NewNotFound()
		}
		return response.NewBadRequest(err)
	}

	if req.Id == 0 {
		return response.NewBadRequest(err)
	}

	req.Body = body
	if err := req.Update(); err != nil {
		return response.NewBadRequest(err)
	}

	cmc := models.NewChannelMessageContainer()
	return response.HandleResultAndError(cmc, cmc.Fetch(id, request.GetQuery(u)))
}

func Get(u *url.URL, h http.Header, _ interface{}) (int, http.Header, interface{}, error) {
	cm, err := getMessageByUrl(u)
	if err != nil {
		return response.NewBadRequest(err)
	}

	if cm.Id == 0 {
		return response.NewNotFound()
	}

	cmc := models.NewChannelMessageContainer()
	return response.HandleResultAndError(cmc, cmc.Fetch(cm.Id, request.GetQuery(u)))
}

func getMessageByUrl(u *url.URL) (*models.ChannelMessage, error) {

	// TODO
	// fmt.Println(`
	// 	------->
	//             ADD SECURTY CHECK FOR VISIBILTY OF THE MESSAGE
	//                         FOR THE REQUESTER
	//     ------->"`,
	// )

	id, err := request.GetURIInt64(u, "id")
	if err != nil {
		return nil, err
	}

	// get url query params
	q := request.GetQuery(u)

	query := &bongo.Query{
		Selector: map[string]interface{}{
			"id": id,
		},
		Pagination: *bongo.NewPagination(1, 0),
	}

	cm := models.NewChannelMessage()
	// add exempt info
	query.AddScope(models.RemoveTrollContent(cm, q.ShowExempt))

	if err := cm.One(query); err != nil {
		return nil, err
	}

	return cm, nil
}

func GetWithRelated(u *url.URL, h http.Header, _ interface{}) (int, http.Header, interface{}, error) {
	cm, err := getMessageByUrl(u)
	if err != nil {
		return response.NewBadRequest(err)
	}

	if cm.Id == 0 {
		return response.NewNotFound()
	}

	q := request.GetQuery(u)

	cmc := models.NewChannelMessageContainer()
	if err := cmc.Fetch(cm.Id, q); err != nil {
		return response.NewBadRequest(err)
	}

	cmc.AddIsInteracted(q).AddIsFollowed(q)

	return response.HandleResultAndError(cmc, cmc.Err)
}

func GetBySlug(u *url.URL, h http.Header, _ interface{}) (int, http.Header, interface{}, error) {
	q := request.GetQuery(u)

	if q.Slug == "" {
		return response.NewBadRequest(errors.New("slug is not set"))
	}

	cm := models.NewChannelMessage()
	if err := cm.BySlug(q); err != nil {
		if err == bongo.RecordNotFound {
			return response.NewNotFound()
		}
		return response.NewBadRequest(err)
	}

	cmc := models.NewChannelMessageContainer()
	if err := cmc.Fetch(cm.Id, q); err != nil {
		return response.NewBadRequest(err)
	}

	cmc.AddIsInteracted(q).AddIsFollowed(q)

	return response.HandleResultAndError(cmc, cmc.Err)
}
<|endoftext|>


<commit_before>package shared

import (
	"errors"
	"fmt"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
	"testing"
)

type mockMetadata struct {
	Metadata
	instanceId string
	region     string
}

func (m *mockMetadata) InstanceID() (string, error) {
	return m.instanceId, nil
}

func (m *mockMetadata) Region() (string, error) {
	return m.region, nil
}

type mockEC2Service struct {
	ec2iface.EC2API
	expectedResourceId string
}

func (svc *mockEC2Service) DescribeTags(input *ec2.DescribeTagsInput) (*ec2.DescribeTagsOutput, error) {

	if *(input.Filters[0].Name) == "resource-id" {

		resourceId := input.Filters[0].Values[0]

		if *resourceId == svc.expectedResourceId {

			return &ec2.DescribeTagsOutput{
				Tags: []*ec2.TagDescription{
					&ec2.TagDescription{
						Key:          aws.String("volume_/dev/sda"),
						ResourceId:   resourceId,
						ResourceType: aws.String("instance"),
						Value:        aws.String("vol-1234567"),
					},
					&ec2.TagDescription{
						Key:          aws.String("volume_/dev/sdb"),
						ResourceId:   resourceId,
						ResourceType: aws.String("instance"),
						Value:        aws.String("vol-54321"),
					},
				},
			}, nil
		}

		return nil, fmt.Errorf("No tags for %s", *resourceId)

	}

	return nil, errors.New("No resource id set")
}

func TestFindAllocatedVolumes(t *testing.T) {

	metadata := &mockMetadata{instanceId: "id-98765", region: "erewhon"}

	var underTest = NewEC2Instance(metadata, &mockEC2Service{expectedResourceId: "id-98765"})

	if volumes, err := underTest.AllocatedVolumes(); err != nil {
		t.Errorf("Shouldn't have failed : got error %s", err.Error())
	} else {
		if len(volumes) != 2 {
			t.Errorf("Should have got 2 allocated volumes, but got %d", len(volumes))
		}

		assertVolumesEqual(t, volumes[0], NewAllocatedVolume("vol-1234567", "/dev/sda", "id-98765", nil))
		assertVolumesEqual(t, volumes[1], NewAllocatedVolume("vol-54321", "/dev/sdb", "id-98765", nil))

	}

}

func assertVolumesEqual(t *testing.T, left *AllocatedVolume, right *AllocatedVolume) {

	if left.DeviceName != right.DeviceName || left.InstanceId != right.InstanceId || left.VolumeId != right.VolumeId {
		t.Errorf("Expected %s but got %s", left.String(), right.String())
	}
}

func TestAttachAllocatedVolumes(t *testing.T) {

	metadata := &mockMetadata{instanceId: "id-98765", region: "erewhon"}

	var underTest = NewEC2Instance(metadata, &mockEC2Service{expectedResourceId: "id-98765"})

	saved := attachVolume
	defer func() { attachVolume = saved }()

	set := make(map[string]struct{}, 2)
	attachVolume = func(volume *AllocatedVolume) { set[volume.VolumeId] = struct{}{} }

	underTest.AttachVolumes()

	if len(set) != 2 {
		t.Errorf("Should have been 2 volumes attached, but %d were", len(set))
	}

	expectedVolumes := []string{"vol-1234567", "vol-54321"}
	for _, expectedVolume := range expectedVolumes {
		if _, ok := set[expectedVolume]; !ok {
			t.Errorf("Volume %s should have been attached, but wasn't", expectedVolume)
		}
	}

}
<commit_msg>Builder for creating tags<commit_after>package shared

import (
	"errors"
	"fmt"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
	"testing"
)

type mockMetadata struct {
	Metadata
	instanceId string
	region     string
}

func (m *mockMetadata) InstanceID() (string, error) {
	return m.instanceId, nil
}

func (m *mockMetadata) Region() (string, error) {
	return m.region, nil
}

type mockEC2Service struct {
	ec2iface.EC2API
	expectedResourceId string
	DescribeTagsFunc   func(input *ec2.DescribeTagsInput) (*ec2.DescribeTagsOutput, error)
}

type DescribeTagsOutputBuilder struct {
	TagDescriptions []*ec2.TagDescription
}

func NewDescribeTagsOutputBuilder() *DescribeTagsOutputBuilder {
	return &DescribeTagsOutputBuilder{}
}

func (builder DescribeTagsOutputBuilder) WithVolume(DeviceName string, InstanceId string, VolumeID string) DescribeTagsOutputBuilder {
	builder.TagDescriptions = append(builder.TagDescriptions, &ec2.TagDescription{
		Key:          aws.String(fmt.Sprintf("volume_%s", DeviceName)),
		ResourceId:   aws.String(InstanceId),
		ResourceType: aws.String("instance"),
		Value:        aws.String(VolumeID),
	})

	return builder
}

func (builder DescribeTagsOutputBuilder) Build() *ec2.DescribeTagsOutput {
	return &ec2.DescribeTagsOutput{
		Tags: builder.TagDescriptions,
	}
}

func (svc *mockEC2Service) DescribeTags(input *ec2.DescribeTagsInput) (*ec2.DescribeTagsOutput, error) {

	expectedOutputBuilder := NewDescribeTagsOutputBuilder().WithVolume("/dev/sda", svc.expectedResourceId, "vol-1234567").WithVolume("/dev/sdb", svc.expectedResourceId, "vol-54321")

	if *(input.Filters[0].Name) == "resource-id" {

		resourceId := input.Filters[0].Values[0]

		if *resourceId == svc.expectedResourceId {

			return expectedOutputBuilder.Build(), nil
		}

		return nil, fmt.Errorf("No tags for %s", *resourceId)

	}

	return nil, errors.New("No resource id set")
}

func TestFindAllocatedVolumes(t *testing.T) {

	metadata := &mockMetadata{instanceId: "id-98765", region: "erewhon"}

	var underTest = NewEC2Instance(metadata, &mockEC2Service{expectedResourceId: "id-98765"})

	if volumes, err := underTest.AllocatedVolumes(); err != nil {
		t.Errorf("Shouldn't have failed : got error %s", err.Error())
	} else {
		if len(volumes) != 2 {
			t.Errorf("Should have got 2 allocated volumes, but got %d", len(volumes))
		}

		assertVolumesEqual(t, volumes[0], NewAllocatedVolume("vol-1234567", "/dev/sda", "id-98765", nil))
		assertVolumesEqual(t, volumes[1], NewAllocatedVolume("vol-54321", "/dev/sdb", "id-98765", nil))

	}

}

func assertVolumesEqual(t *testing.T, left *AllocatedVolume, right *AllocatedVolume) {

	if left.DeviceName != right.DeviceName || left.InstanceId != right.InstanceId || left.VolumeId != right.VolumeId {
		t.Errorf("Expected %s but got %s", left.String(), right.String())
	}
}

func TestAttachAllocatedVolumes(t *testing.T) {

	metadata := &mockMetadata{instanceId: "id-98765", region: "erewhon"}

	var underTest = NewEC2Instance(metadata, &mockEC2Service{expectedResourceId: "id-98765"})

	saved := attachVolume
	defer func() { attachVolume = saved }()

	set := make(map[string]struct{}, 2)
	attachVolume = func(volume *AllocatedVolume) { set[volume.VolumeId] = struct{}{} }

	underTest.AttachVolumes()

	if len(set) != 2 {
		t.Errorf("Should have been 2 volumes attached, but %d were", len(set))
	}

	expectedVolumes := []string{"vol-1234567", "vol-54321"}
	for _, expectedVolume := range expectedVolumes {
		if _, ok := set[expectedVolume]; !ok {
			t.Errorf("Volume %s should have been attached, but wasn't", expectedVolume)
		}
	}

}
<|endoftext|>


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

package fakestorage

import (
	"context"
	"crypto/tls"
	"fmt"
	"net"
	"net/http"
	"net/http/httptest"
	"sync"

	"cloud.google.com/go/storage"
	"github.com/fsouza/fake-gcs-server/internal/backend"
	"github.com/gorilla/mux"
	"google.golang.org/api/option"
)

// Server is the fake server.
//
// It provides a fake implementation of the Google Cloud Storage API.
type Server struct {
	backend   backend.Storage
	uploads   map[string]Object
	transport http.RoundTripper
	ts        *httptest.Server
	mux       *mux.Router
	mtx       sync.RWMutex
}

// NewServer creates a new instance of the server, pre-loaded with the given
// objects.
func NewServer(objects []Object) *Server {
	s, _ := NewServerWithOptions(Options{
		InitialObjects: objects,
	})
	return s
}

// NewServerWithHostPort creates a new server that listens on a custom host and port
func NewServerWithHostPort(objects []Object, host string, port uint16) (*Server, error) {
	return NewServerWithOptions(Options{
		InitialObjects: objects,
		Host:           host,
		Port:           port,
	})
}

// Options are used to configure the server on creation
type Options struct {
	InitialObjects []Object
	StorageRoot    string
	Host           string
	Port           uint16

	// when set to true, the server will not actually start a TCP listener,
	// client requests will get processed by an internal mocked transport.
	NoListener bool
}

// NewServerWithOptions creates a new server with custom options
func NewServerWithOptions(options Options) (*Server, error) {
	s, err := newUnstartedServer(options.InitialObjects, options.StorageRoot, !options.NoListener)
	if err != nil {
		return nil, err
	}
	if options.NoListener {
		s.setTransportToMux()
		return s, nil
	}
	var addr string
	if options.Port != 0 {
		addr = fmt.Sprintf("%s:%d", options.Host, options.Port)
		l, err := net.Listen("tcp", addr)
		if err != nil {
			return nil, err
		}
		s.ts.Listener.Close()
		s.ts.Listener = l
		s.ts.StartTLS()
		s.setTransportToAddr(addr)
	} else {
		s.setTransportToAddr(s.ts.Listener.Addr().String())
		s.ts.StartTLS()
	}
	return s, nil
}

func newUnstartedServer(objects []Object, storageRoot string, listen bool) (*Server, error) {
	backendObjects := toBackendObjects(objects)
	var backendStorage backend.Storage
	var err error
	if storageRoot != "" {
		backendStorage, err = backend.NewStorageFS(backendObjects, storageRoot)
	} else {
		backendStorage = backend.NewStorageMemory(backendObjects)
	}
	if err != nil {
		return nil, err
	}
	s := Server{
		backend: backendStorage,
		uploads: make(map[string]Object),
	}
	s.buildMuxer()
	if listen {
		s.ts = httptest.NewUnstartedServer(s.mux)
	}
	return &s, nil
}

func (s *Server) setTransportToAddr(addr string) {
	// #nosec
	tlsConfig := tls.Config{InsecureSkipVerify: true}
	s.transport = &http.Transport{
		TLSClientConfig: &tlsConfig,
		DialTLS: func(string, string) (net.Conn, error) {
			return tls.Dial("tcp", addr, &tlsConfig)
		},
	}
}

func (s *Server) setTransportToMux() {
	s.transport = &muxTransport{router: s.mux}
}

func (s *Server) buildMuxer() {
	s.mux = mux.NewRouter()
	s.mux.Host("storage.googleapis.com").Path("/{bucketName}/{objectName:.+}").Methods("GET", "HEAD").HandlerFunc(s.downloadObject)
	s.mux.Host("{bucketName}.storage.googleapis.com").Path("/{objectName:.+}").Methods("GET", "HEAD").HandlerFunc(s.downloadObject)
	r := s.mux.PathPrefix("/storage/v1").Subrouter()
	r.Path("/b").Methods("GET").HandlerFunc(s.listBuckets)
	r.Path("/b/{bucketName}").Methods("GET").HandlerFunc(s.getBucket)
	r.Path("/b/{bucketName}/o").Methods("GET").HandlerFunc(s.listObjects)
	r.Path("/b/{bucketName}/o").Methods("POST").HandlerFunc(s.insertObject)
	r.Path("/b/{bucketName}/o/{objectName:.+}").Methods("GET").HandlerFunc(s.getObject)
	r.Path("/b/{bucketName}/o/{objectName:.+}").Methods("DELETE").HandlerFunc(s.deleteObject)
	r.Path("/b/{sourceBucket}/o/{sourceObject:.+}/rewriteTo/b/{destinationBucket}/o/{destinationObject:.+}").HandlerFunc(s.rewriteObject)
	s.mux.Path("/upload/storage/v1/b/{bucketName}/o").Methods("POST").HandlerFunc(s.insertObject)
	s.mux.Path("/upload/resumable/{uploadId}").Methods("PUT", "POST").HandlerFunc(s.uploadFileContent)
}

// Stop stops the server, closing all connections.
func (s *Server) Stop() {
	if s.ts != nil {
		if transport, ok := s.transport.(*http.Transport); ok {
			transport.CloseIdleConnections()
		}
		s.ts.Close()
	}
}

// URL returns the server URL.
func (s *Server) URL() string {
	if s.ts != nil {
		return s.ts.URL
	}
	return ""
}

// HTTPClient returns an HTTP client configured to talk to the server.
func (s *Server) HTTPClient() *http.Client {
	return &http.Client{Transport: s.transport}
}

// Client returns a GCS client configured to talk to the server.
func (s *Server) Client() *storage.Client {
	opt := option.WithHTTPClient(s.HTTPClient())
	client, _ := storage.NewClient(context.Background(), opt)
	return client
}
<commit_msg>fakestorage/server: improve organization and naming in the constructor<commit_after>// Copyright 2017 Francisco Souza. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package fakestorage

import (
	"context"
	"crypto/tls"
	"fmt"
	"net"
	"net/http"
	"net/http/httptest"
	"sync"

	"cloud.google.com/go/storage"
	"github.com/fsouza/fake-gcs-server/internal/backend"
	"github.com/gorilla/mux"
	"google.golang.org/api/option"
)

// Server is the fake server.
//
// It provides a fake implementation of the Google Cloud Storage API.
type Server struct {
	backend   backend.Storage
	uploads   map[string]Object
	transport http.RoundTripper
	ts        *httptest.Server
	mux       *mux.Router
	mtx       sync.RWMutex
}

// NewServer creates a new instance of the server, pre-loaded with the given
// objects.
func NewServer(objects []Object) *Server {
	s, _ := NewServerWithOptions(Options{
		InitialObjects: objects,
	})
	return s
}

// NewServerWithHostPort creates a new server that listens on a custom host and port
func NewServerWithHostPort(objects []Object, host string, port uint16) (*Server, error) {
	return NewServerWithOptions(Options{
		InitialObjects: objects,
		Host:           host,
		Port:           port,
	})
}

// Options are used to configure the server on creation
type Options struct {
	InitialObjects []Object
	StorageRoot    string
	Host           string
	Port           uint16

	// when set to true, the server will not actually start a TCP listener,
	// client requests will get processed by an internal mocked transport.
	NoListener bool
}

// NewServerWithOptions creates a new server with custom options
func NewServerWithOptions(options Options) (*Server, error) {
	s, err := newServer(options.InitialObjects, options.StorageRoot)
	if err != nil {
		return nil, err
	}
	if options.NoListener {
		s.setTransportToMux()
		return s, nil
	}

	s.ts = httptest.NewUnstartedServer(s.mux)
	if options.Port != 0 {
		addr := fmt.Sprintf("%s:%d", options.Host, options.Port)
		l, err := net.Listen("tcp", addr)
		if err != nil {
			return nil, err
		}
		s.ts.Listener.Close()
		s.ts.Listener = l
		s.ts.StartTLS()
	} else {
		s.ts.StartTLS()
	}
	s.setTransportToAddr(s.ts.Listener.Addr().String())
	return s, nil
}

func newServer(objects []Object, storageRoot string) (*Server, error) {
	backendObjects := toBackendObjects(objects)
	var backendStorage backend.Storage
	var err error
	if storageRoot != "" {
		backendStorage, err = backend.NewStorageFS(backendObjects, storageRoot)
	} else {
		backendStorage = backend.NewStorageMemory(backendObjects)
	}
	if err != nil {
		return nil, err
	}
	s := Server{
		backend: backendStorage,
		uploads: make(map[string]Object),
	}
	s.buildMuxer()
	return &s, nil
}

func (s *Server) setTransportToAddr(addr string) {
	// #nosec
	tlsConfig := tls.Config{InsecureSkipVerify: true}
	s.transport = &http.Transport{
		TLSClientConfig: &tlsConfig,
		DialTLS: func(string, string) (net.Conn, error) {
			return tls.Dial("tcp", addr, &tlsConfig)
		},
	}
}

func (s *Server) setTransportToMux() {
	s.transport = &muxTransport{router: s.mux}
}

func (s *Server) buildMuxer() {
	s.mux = mux.NewRouter()
	s.mux.Host("storage.googleapis.com").Path("/{bucketName}/{objectName:.+}").Methods("GET", "HEAD").HandlerFunc(s.downloadObject)
	s.mux.Host("{bucketName}.storage.googleapis.com").Path("/{objectName:.+}").Methods("GET", "HEAD").HandlerFunc(s.downloadObject)
	r := s.mux.PathPrefix("/storage/v1").Subrouter()
	r.Path("/b").Methods("GET").HandlerFunc(s.listBuckets)
	r.Path("/b/{bucketName}").Methods("GET").HandlerFunc(s.getBucket)
	r.Path("/b/{bucketName}/o").Methods("GET").HandlerFunc(s.listObjects)
	r.Path("/b/{bucketName}/o").Methods("POST").HandlerFunc(s.insertObject)
	r.Path("/b/{bucketName}/o/{objectName:.+}").Methods("GET").HandlerFunc(s.getObject)
	r.Path("/b/{bucketName}/o/{objectName:.+}").Methods("DELETE").HandlerFunc(s.deleteObject)
	r.Path("/b/{sourceBucket}/o/{sourceObject:.+}/rewriteTo/b/{destinationBucket}/o/{destinationObject:.+}").HandlerFunc(s.rewriteObject)
	s.mux.Path("/upload/storage/v1/b/{bucketName}/o").Methods("POST").HandlerFunc(s.insertObject)
	s.mux.Path("/upload/resumable/{uploadId}").Methods("PUT", "POST").HandlerFunc(s.uploadFileContent)
}

// Stop stops the server, closing all connections.
func (s *Server) Stop() {
	if s.ts != nil {
		if transport, ok := s.transport.(*http.Transport); ok {
			transport.CloseIdleConnections()
		}
		s.ts.Close()
	}
}

// URL returns the server URL.
func (s *Server) URL() string {
	if s.ts != nil {
		return s.ts.URL
	}
	return ""
}

// HTTPClient returns an HTTP client configured to talk to the server.
func (s *Server) HTTPClient() *http.Client {
	return &http.Client{Transport: s.transport}
}

// Client returns a GCS client configured to talk to the server.
func (s *Server) Client() *storage.Client {
	opt := option.WithHTTPClient(s.HTTPClient())
	client, _ := storage.NewClient(context.Background(), opt)
	return client
}
<|endoftext|>


<commit_before>package ovf

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

var data = []byte(`<?xml version="1.0" encoding="UTF-8"?>
<Environment
     xmlns="http://schemas.dmtf.org/ovf/environment/1"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:oe="http://schemas.dmtf.org/ovf/environment/1"
     xmlns:ve="http://www.vmware.com/schema/ovfenv"
     oe:id=""
     ve:vCenterId="vm-12345">
   <PlatformSection>
      <Kind>VMware ESXi</Kind>
      <Version>5.5.0</Version>
      <Vendor>VMware, Inc.</Vendor>
      <Locale>en</Locale>
   </PlatformSection>
   <PropertySection>
         <Property oe:key="foo" oe:value="42"/>
         <Property oe:key="bar" oe:value="0"/>
   </PropertySection>
   <ve:EthernetAdapterSection>
      <ve:Adapter ve:mac="00:00:00:00:00:00" ve:network="foo" ve:unitNumber="7"/>
   </ve:EthernetAdapterSection>
</Environment>`)

func TestOvfEnvProperties(t *testing.T) {
	env := ReadEnvironment(data)
	props := env.Properties

	var val string
	var ok bool

	val, ok = props["foo"]
	assert.True(t, ok)
	assert.Equal(t, val, "42")

	val, ok = props["bar"]
	assert.True(t, ok)
	assert.Equal(t, val, "0")
}

func TestOvfEnvPlatform(t *testing.T) {
	env := ReadEnvironment(data)
	platform := env.Platform

	assert.Equal(t, platform.Kind, "VMware ESXi")
	assert.Equal(t, platform.Version, "5.5.0")
	assert.Equal(t, platform.Vendor, "VMware, Inc.")
	assert.Equal(t, platform.Locale, "en")
}
<commit_msg>use goconvey<commit_after>package ovf

import (
	"testing"

	. "github.com/smartystreets/goconvey/convey"
)

var data_vsphere = []byte(`<?xml version="1.0" encoding="UTF-8"?>
<Environment
     xmlns="http://schemas.dmtf.org/ovf/environment/1"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:oe="http://schemas.dmtf.org/ovf/environment/1"
     xmlns:ve="http://www.vmware.com/schema/ovfenv"
     oe:id=""
     ve:vCenterId="vm-12345">
   <PlatformSection>
      <Kind>VMware ESXi</Kind>
      <Version>5.5.0</Version>
      <Vendor>VMware, Inc.</Vendor>
      <Locale>en</Locale>
   </PlatformSection>
   <PropertySection>
         <Property oe:key="foo" oe:value="42"/>
         <Property oe:key="bar" oe:value="0"/>
   </PropertySection>
   <ve:EthernetAdapterSection>
      <ve:Adapter ve:mac="00:00:00:00:00:00" ve:network="foo" ve:unitNumber="7"/>
   </ve:EthernetAdapterSection>
</Environment>`)

var data_vapprun = []byte(`<?xml version="1.0" encoding="UTF-8"?>
<Environment xmlns="http://schemas.dmtf.org/ovf/environment/1"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:oe="http://schemas.dmtf.org/ovf/environment/1"
     oe:id="CoreOS-vmw">
   <PlatformSection>
      <Kind>vapprun</Kind>
      <Version>1.0</Version>
      <Vendor>VMware, Inc.</Vendor>
      <Locale>en_US</Locale>
   </PlatformSection>
   <PropertySection>
      <Property oe:key="foo" oe:value="42"/>
      <Property oe:key="bar" oe:value="0"/>
      <Property oe:key="guestinfo.user_data.url" oe:value="https://gist.githubusercontent.com/sigma/5a64aac1693da9ca70d2/raw/plop.yaml"/>
      <Property oe:key="guestinfo.user_data.doc" oe:value=""/>
      <Property oe:key="guestinfo.meta_data.url" oe:value=""/>
      <Property oe:key="guestinfo.meta_data.doc" oe:value=""/>
   </PropertySection>
</Environment>`)

func TestOvfEnvProperties(t *testing.T) {

	var testerFunc = func(env *OvfEnvironment) {
		props := env.Properties

		var val string
		var ok bool
		Convey(`Property "foo"`, func() {
			val, ok = props["foo"]
			So(ok, ShouldBeTrue)
			So(val, ShouldEqual, "42")
		})

		Convey(`Property "bar"`, func() {
			val, ok = props["bar"]
			So(ok, ShouldBeTrue)
			So(val, ShouldEqual, "0")
		})
	}

	Convey("With vSphere environment", t, func() {
		env := ReadEnvironment(data_vsphere)
		testerFunc(env)
	})

	Convey("With vAppRun environment", t, func() {
		env := ReadEnvironment(data_vapprun)
		testerFunc(env)
	})
}

func TestOvfEnvPlatform(t *testing.T) {
	Convey("With vSphere environment", t, func() {
		env := ReadEnvironment(data_vsphere)
		platform := env.Platform

		So(platform.Kind, ShouldEqual, "VMware ESXi")
		So(platform.Version, ShouldEqual, "5.5.0")
		So(platform.Vendor, ShouldEqual, "VMware, Inc.")
		So(platform.Locale, ShouldEqual, "en")
	})
}

func TestVappRunUserDataUrl(t *testing.T) {
	Convey("With vAppRun environment", t, func() {
		env := ReadEnvironment(data_vapprun)
		props := env.Properties

		var val string
		var ok bool

		val, ok = props["guestinfo.user_data.url"]
		So(ok, ShouldBeTrue)
		So(val, ShouldEqual, "https://gist.githubusercontent.com/sigma/5a64aac1693da9ca70d2/raw/plop.yaml")
	})
}
<|endoftext|>


<commit_before>package model

import "google.golang.org/cloud/datastore"

//go:generate generator

// Task is a concrete piece of work that cannot
// be split any further.
//
// This type is very general and can be implemented in vrious
// ways, accordingly implementing logic to make this Task comparable
// to others with respect to it's SkillWeights.
type Task struct {
	// Returns details on the assignment that is covered by this task.
	Assignment Assignment

	// Says what skills are needed/exercised to complete
	// the Task.
	SkillWeights SkillWeights

	// Refers to some logic that looks at the Submissions
	// of this task and produces a set of skills that
	// represent how well the user did in doing this Task.
	// It is to be weighted by Skillweights.
	Tasker int

	Languages []string `datastore:",noindex"`

	datastore.PropertyLoadSaver
}
<commit_msg>model: Make task not embed PLS<commit_after>package model

//go:generate generator

// Task is a concrete piece of work that cannot
// be split any further.
//
// This type is very general and can be implemented in vrious
// ways, accordingly implementing logic to make this Task comparable
// to others with respect to it's SkillWeights.
type Task struct {
	// Returns details on the assignment that is covered by this task.
	Assignment Assignment

	// Says what skills are needed/exercised to complete
	// the Task.
	SkillWeights SkillWeights

	// Refers to some logic that looks at the Submissions
	// of this task and produces a set of skills that
	// represent how well the user did in doing this Task.
	// It is to be weighted by SkillWeights.
	Tasker int

	Languages []string `datastore:",noindex"`
}
<|endoftext|>


<commit_before>package arn

import (
	"errors"
	"reflect"
	"strings"

	"github.com/aerogo/aero"
	"github.com/aerogo/api"
)

// Force interface implementations
var (
	_ api.Newable  = (*SoundTrack)(nil)
	_ api.Editable = (*SoundTrack)(nil)
	_ Publishable  = (*SoundTrack)(nil)
)

// Actions
func init() {
	API.RegisterActions("SoundTrack", []*api.Action{
		// Publish
		PublishAction(),

		// Unpublish
		UnpublishAction(),
	})
}

// Create sets the data for a new soundtrack with data we received from the API request.
func (soundtrack *SoundTrack) Create(ctx *aero.Context) error {
	user := GetUserFromContext(ctx)

	if user == nil {
		return errors.New("Not logged in")
	}

	soundtrack.ID = GenerateID("SoundTrack")
	soundtrack.Likes = []string{}
	soundtrack.Created = DateTimeUTC()
	soundtrack.CreatedBy = user.ID
	soundtrack.Media = []*ExternalMedia{}
	soundtrack.Tags = []string{}

	return soundtrack.Unpublish()
}

// Edit updates the external media object.
func (soundtrack *SoundTrack) Edit(ctx *aero.Context, key string, value reflect.Value, newValue reflect.Value) (bool, error) {
	if strings.HasPrefix(key, "Media[") && strings.HasSuffix(key, ".Service") {
		newService := newValue.String()

		if !Contains(ExternalMediaServices, newService) {
			return true, errors.New("Invalid service name")
		}

		value.SetString(newService)
		return true, nil
	}

	return false, nil
}

// AfterEdit updates the metadata.
func (soundtrack *SoundTrack) AfterEdit(ctx *aero.Context) error {
	soundtrack.Edited = DateTimeUTC()
	soundtrack.EditedBy = GetUserFromContext(ctx).ID
	return nil
}

// Authorize returns an error if the given API POST request is not authorized.
func (soundtrack *SoundTrack) Authorize(ctx *aero.Context, action string) error {
	if !ctx.HasSession() {
		return errors.New("Neither logged in nor in session")
	}

	return nil
}

// Save saves the soundtrack object in the database.
func (soundtrack *SoundTrack) Save() error {
	return DB.Set("SoundTrack", soundtrack.ID, soundtrack)
}
<commit_msg>Added soundtrack deletion<commit_after>package arn

import (
	"errors"
	"reflect"
	"strings"

	"github.com/aerogo/aero"
	"github.com/aerogo/api"
)

// Force interface implementations
var (
	_ api.Newable   = (*SoundTrack)(nil)
	_ api.Editable  = (*SoundTrack)(nil)
	_ api.Deletable = (*SoundTrack)(nil)
	_ Publishable   = (*SoundTrack)(nil)
)

// Actions
func init() {
	API.RegisterActions("SoundTrack", []*api.Action{
		// Publish
		PublishAction(),

		// Unpublish
		UnpublishAction(),
	})
}

// Create sets the data for a new soundtrack with data we received from the API request.
func (soundtrack *SoundTrack) Create(ctx *aero.Context) error {
	user := GetUserFromContext(ctx)

	if user == nil {
		return errors.New("Not logged in")
	}

	soundtrack.ID = GenerateID("SoundTrack")
	soundtrack.Likes = []string{}
	soundtrack.Created = DateTimeUTC()
	soundtrack.CreatedBy = user.ID
	soundtrack.Media = []*ExternalMedia{}
	soundtrack.Tags = []string{}

	return soundtrack.Unpublish()
}

// Edit updates the external media object.
func (soundtrack *SoundTrack) Edit(ctx *aero.Context, key string, value reflect.Value, newValue reflect.Value) (bool, error) {
	if strings.HasPrefix(key, "Media[") && strings.HasSuffix(key, ".Service") {
		newService := newValue.String()

		if !Contains(ExternalMediaServices, newService) {
			return true, errors.New("Invalid service name")
		}

		value.SetString(newService)
		return true, nil
	}

	return false, nil
}

// AfterEdit updates the metadata.
func (soundtrack *SoundTrack) AfterEdit(ctx *aero.Context) error {
	soundtrack.Edited = DateTimeUTC()
	soundtrack.EditedBy = GetUserFromContext(ctx).ID
	return nil
}

// Delete deletes the object from the database.
func (soundtrack *SoundTrack) Delete() error {
	_, err := DB.Delete("SoundTrack", soundtrack.ID)
	return err
}

// Authorize returns an error if the given API POST request is not authorized.
func (soundtrack *SoundTrack) Authorize(ctx *aero.Context, action string) error {
	if !ctx.HasSession() {
		return errors.New("Neither logged in nor in session")
	}

	return nil
}

// Save saves the soundtrack object in the database.
func (soundtrack *SoundTrack) Save() error {
	return DB.Set("SoundTrack", soundtrack.ID, soundtrack)
}
<|endoftext|>


<commit_before>// Copyright 2018 Google LLC
//
// 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 dhcp implements a DHCP client and server as described in RFC 2131.
package dhcp

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"time"

	"gvisor.googlesource.com/gvisor/pkg/tcpip"
)

// Config is standard DHCP configuration.
type Config struct {
	Error         error
	ServerAddress tcpip.Address     // address of the server
	SubnetMask    tcpip.AddressMask // client address subnet mask
	Gateway       tcpip.Address     // client default gateway
	DNS           []tcpip.Address   // client DNS server addresses
	LeaseLength   time.Duration     // length of the address lease
}

func (cfg *Config) decode(opts []option) error {
	*cfg = Config{}
	for _, opt := range opts {
		b := opt.body
		if !opt.code.lenValid(len(b)) {
			// TODO: s/%v/%s/ when `go vet` is smarter.
			return fmt.Errorf("%v: bad length: %d", opt.code, len(b))
		}
		switch opt.code {
		case optLeaseTime:
			t := binary.BigEndian.Uint32(b)
			cfg.LeaseLength = time.Duration(t) * time.Second
		case optSubnetMask:
			cfg.SubnetMask = tcpip.AddressMask(b)
		case optDHCPServer:
			cfg.ServerAddress = tcpip.Address(b)
		case optDefaultGateway:
			cfg.Gateway = tcpip.Address(b)
		case optDomainNameServer:
			for ; len(b) > 0; b = b[4:] {
				if len(b) < 4 {
					return fmt.Errorf("DNS bad length: %d", len(b))
				}
				cfg.DNS = append(cfg.DNS, tcpip.Address(b[:4]))
			}
		}
	}
	return nil
}

func (cfg Config) encode() (opts []option) {
	if cfg.ServerAddress != "" {
		opts = append(opts, option{optDHCPServer, []byte(cfg.ServerAddress)})
	}
	if cfg.SubnetMask != "" {
		opts = append(opts, option{optSubnetMask, []byte(cfg.SubnetMask)})
	}
	if cfg.Gateway != "" {
		opts = append(opts, option{optDefaultGateway, []byte(cfg.Gateway)})
	}
	if len(cfg.DNS) > 0 {
		dns := make([]byte, 0, 4*len(cfg.DNS))
		for _, addr := range cfg.DNS {
			dns = append(dns, addr...)
		}
		opts = append(opts, option{optDomainNameServer, dns})
	}
	if l := cfg.LeaseLength / time.Second; l != 0 {
		v := make([]byte, 4)
		v[0] = byte(l >> 24)
		v[1] = byte(l >> 16)
		v[2] = byte(l >> 8)
		v[3] = byte(l >> 0)
		opts = append(opts, option{optLeaseTime, v})
	}
	return opts
}

const (
	// ServerPort is the well-known UDP port number for a DHCP server.
	ServerPort = 67
	// ClientPort is the well-known UDP port number for a DHCP client.
	ClientPort = 68
)

var magicCookie = []byte{99, 130, 83, 99} // RFC 1497

type xid uint32

type header []byte

func (h header) init() {
	h[1] = 0x01       // htype
	h[2] = 0x06       // hlen
	h[3] = 0x00       // hops
	h[8], h[9] = 0, 0 // secs
	copy(h[236:240], magicCookie)
}

func (h header) isValid() bool {
	if len(h) < 241 {
		return false
	}
	if o := h.op(); o != opRequest && o != opReply {
		return false
	}
	if h[1] != 0x01 || h[2] != 0x06 {
		return false
	}
	return bytes.Equal(h[236:240], magicCookie)
}

func (h header) op() op           { return op(h[0]) }
func (h header) setOp(o op)       { h[0] = byte(o) }
func (h header) xidbytes() []byte { return h[4:8] }
func (h header) xid() xid         { return xid(h[4])<<24 | xid(h[5])<<16 | xid(h[6])<<8 | xid(h[7]) }
func (h header) setBroadcast()    { h[10], h[11] = 0x80, 0x00 } // flags top bit
func (h header) ciaddr() []byte   { return h[12:16] }
func (h header) yiaddr() []byte   { return h[16:20] }
func (h header) siaddr() []byte   { return h[20:24] }
func (h header) giaddr() []byte   { return h[24:28] }
func (h header) chaddr() []byte   { return h[28:44] }
func (h header) sname() []byte    { return h[44:108] }
func (h header) file() []byte     { return h[108:236] }

func (h header) options() (opts options, err error) {
	i := headerBaseSize
	for i < len(h) {
		if h[i] == 0 {
			i++
			continue
		}
		if h[i] == 255 {
			break
		}
		if len(h) <= i+1 {
			return nil, fmt.Errorf("option missing length")
		}
		optlen := int(h[i+1])
		if len(h) < i+2+optlen {
			return nil, fmt.Errorf("option %v too long i=%d, optlen=%d", optionCode(h[i]), i, optlen)
		}
		opts = append(opts, option{
			code: optionCode(h[i]),
			body: h[i+2 : i+2+optlen],
		})
		i += 2 + optlen
	}
	return opts, nil
}

func (h header) setOptions(opts []option) {
	i := headerBaseSize
	for _, opt := range opts {
		h[i] = byte(opt.code)
		h[i+1] = byte(len(opt.body))
		copy(h[i+2:i+2+len(opt.body)], opt.body)
		i += 2 + len(opt.body)
	}
	h[i] = 255 // End option
	i++
	for ; i < len(h); i++ {
		h[i] = 0
	}
}

// headerBaseSize is the size of a DHCP packet, including the magic cookie.
//
// Note that a DHCP packet is required to have an 'end' option that takes
// up an extra byte, so the minimum DHCP packet size is headerBaseSize + 1.
const headerBaseSize = 240

type option struct {
	code optionCode
	body []byte
}

type optionCode byte

const (
	optSubnetMask       optionCode = 1
	optDefaultGateway   optionCode = 3
	optDomainNameServer optionCode = 6
	optDomainName       optionCode = 15
	optReqIPAddr        optionCode = 50
	optLeaseTime        optionCode = 51
	optDHCPMsgType      optionCode = 53 // dhcpMsgType
	optDHCPServer       optionCode = 54
	optParamReq         optionCode = 55
	optMessage          optionCode = 56
	optClientID         optionCode = 61
)

func (code optionCode) lenValid(l int) bool {
	switch code {
	case optSubnetMask, optDefaultGateway,
		optReqIPAddr, optLeaseTime, optDHCPServer:
		return l == 4
	case optDHCPMsgType:
		return l == 1
	case optDomainNameServer:
		return l%4 == 0
	case optMessage, optDomainName, optClientID:
		return l >= 1
	case optParamReq:
		return true // no fixed length
	default:
		return true // unknown option, assume ok
	}
}

type options []option

func (opts options) dhcpMsgType() (dhcpMsgType, error) {
	for _, opt := range opts {
		if opt.code == optDHCPMsgType {
			if len(opt.body) != 1 {
				// TODO: s/%v/%s/ when `go vet` is smarter.
				return 0, fmt.Errorf("%v: bad length: %d", opt.code, len(opt.body))
			}
			v := opt.body[0]
			if v <= 0 || v >= 8 {
				return 0, fmt.Errorf("DHCP bad length: %d", len(opt.body))
			}
			return dhcpMsgType(v), nil
		}
	}
	return 0, nil
}

func (opts options) message() string {
	for _, opt := range opts {
		if opt.code == optMessage {
			return string(opt.body)
		}
	}
	return ""
}

func (opts options) len() int {
	l := 0
	for _, opt := range opts {
		l += 1 + 1 + len(opt.body) // code + len + body
	}
	return l + 1 // extra byte for 'pad' option
}

type op byte

const (
	opRequest op = 0x01
	opReply   op = 0x02
)

// dhcpMsgType is the DHCP Message Type from RFC 1533, section 9.4.
type dhcpMsgType byte

const (
	dhcpDISCOVER dhcpMsgType = 1
	dhcpOFFER    dhcpMsgType = 2
	dhcpREQUEST  dhcpMsgType = 3
	dhcpDECLINE  dhcpMsgType = 4
	dhcpACK      dhcpMsgType = 5
	dhcpNAK      dhcpMsgType = 6
	dhcpRELEASE  dhcpMsgType = 7
)
<commit_msg>Resolve stringer TODO<commit_after>// Copyright 2018 Google LLC
//
// 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 dhcp implements a DHCP client and server as described in RFC 2131.
package dhcp

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"time"

	"gvisor.googlesource.com/gvisor/pkg/tcpip"
)

// Config is standard DHCP configuration.
type Config struct {
	Error         error
	ServerAddress tcpip.Address     // address of the server
	SubnetMask    tcpip.AddressMask // client address subnet mask
	Gateway       tcpip.Address     // client default gateway
	DNS           []tcpip.Address   // client DNS server addresses
	LeaseLength   time.Duration     // length of the address lease
}

func (cfg *Config) decode(opts []option) error {
	*cfg = Config{}
	for _, opt := range opts {
		b := opt.body
		if !opt.code.lenValid(len(b)) {
			return fmt.Errorf("%s: bad length: %d", opt.code, len(b))
		}
		switch opt.code {
		case optLeaseTime:
			t := binary.BigEndian.Uint32(b)
			cfg.LeaseLength = time.Duration(t) * time.Second
		case optSubnetMask:
			cfg.SubnetMask = tcpip.AddressMask(b)
		case optDHCPServer:
			cfg.ServerAddress = tcpip.Address(b)
		case optDefaultGateway:
			cfg.Gateway = tcpip.Address(b)
		case optDomainNameServer:
			for ; len(b) > 0; b = b[4:] {
				if len(b) < 4 {
					return fmt.Errorf("DNS bad length: %d", len(b))
				}
				cfg.DNS = append(cfg.DNS, tcpip.Address(b[:4]))
			}
		}
	}
	return nil
}

func (cfg Config) encode() (opts []option) {
	if cfg.ServerAddress != "" {
		opts = append(opts, option{optDHCPServer, []byte(cfg.ServerAddress)})
	}
	if cfg.SubnetMask != "" {
		opts = append(opts, option{optSubnetMask, []byte(cfg.SubnetMask)})
	}
	if cfg.Gateway != "" {
		opts = append(opts, option{optDefaultGateway, []byte(cfg.Gateway)})
	}
	if len(cfg.DNS) > 0 {
		dns := make([]byte, 0, 4*len(cfg.DNS))
		for _, addr := range cfg.DNS {
			dns = append(dns, addr...)
		}
		opts = append(opts, option{optDomainNameServer, dns})
	}
	if l := cfg.LeaseLength / time.Second; l != 0 {
		v := make([]byte, 4)
		v[0] = byte(l >> 24)
		v[1] = byte(l >> 16)
		v[2] = byte(l >> 8)
		v[3] = byte(l >> 0)
		opts = append(opts, option{optLeaseTime, v})
	}
	return opts
}

const (
	// ServerPort is the well-known UDP port number for a DHCP server.
	ServerPort = 67
	// ClientPort is the well-known UDP port number for a DHCP client.
	ClientPort = 68
)

var magicCookie = []byte{99, 130, 83, 99} // RFC 1497

type xid uint32

type header []byte

func (h header) init() {
	h[1] = 0x01       // htype
	h[2] = 0x06       // hlen
	h[3] = 0x00       // hops
	h[8], h[9] = 0, 0 // secs
	copy(h[236:240], magicCookie)
}

func (h header) isValid() bool {
	if len(h) < 241 {
		return false
	}
	if o := h.op(); o != opRequest && o != opReply {
		return false
	}
	if h[1] != 0x01 || h[2] != 0x06 {
		return false
	}
	return bytes.Equal(h[236:240], magicCookie)
}

func (h header) op() op           { return op(h[0]) }
func (h header) setOp(o op)       { h[0] = byte(o) }
func (h header) xidbytes() []byte { return h[4:8] }
func (h header) xid() xid         { return xid(h[4])<<24 | xid(h[5])<<16 | xid(h[6])<<8 | xid(h[7]) }
func (h header) setBroadcast()    { h[10], h[11] = 0x80, 0x00 } // flags top bit
func (h header) ciaddr() []byte   { return h[12:16] }
func (h header) yiaddr() []byte   { return h[16:20] }
func (h header) siaddr() []byte   { return h[20:24] }
func (h header) giaddr() []byte   { return h[24:28] }
func (h header) chaddr() []byte   { return h[28:44] }
func (h header) sname() []byte    { return h[44:108] }
func (h header) file() []byte     { return h[108:236] }

func (h header) options() (opts options, err error) {
	i := headerBaseSize
	for i < len(h) {
		if h[i] == 0 {
			i++
			continue
		}
		if h[i] == 255 {
			break
		}
		if len(h) <= i+1 {
			return nil, fmt.Errorf("option missing length")
		}
		optlen := int(h[i+1])
		if len(h) < i+2+optlen {
			return nil, fmt.Errorf("option %v too long i=%d, optlen=%d", optionCode(h[i]), i, optlen)
		}
		opts = append(opts, option{
			code: optionCode(h[i]),
			body: h[i+2 : i+2+optlen],
		})
		i += 2 + optlen
	}
	return opts, nil
}

func (h header) setOptions(opts []option) {
	i := headerBaseSize
	for _, opt := range opts {
		h[i] = byte(opt.code)
		h[i+1] = byte(len(opt.body))
		copy(h[i+2:i+2+len(opt.body)], opt.body)
		i += 2 + len(opt.body)
	}
	h[i] = 255 // End option
	i++
	for ; i < len(h); i++ {
		h[i] = 0
	}
}

// headerBaseSize is the size of a DHCP packet, including the magic cookie.
//
// Note that a DHCP packet is required to have an 'end' option that takes
// up an extra byte, so the minimum DHCP packet size is headerBaseSize + 1.
const headerBaseSize = 240

type option struct {
	code optionCode
	body []byte
}

type optionCode byte

const (
	optSubnetMask       optionCode = 1
	optDefaultGateway   optionCode = 3
	optDomainNameServer optionCode = 6
	optDomainName       optionCode = 15
	optReqIPAddr        optionCode = 50
	optLeaseTime        optionCode = 51
	optDHCPMsgType      optionCode = 53 // dhcpMsgType
	optDHCPServer       optionCode = 54
	optParamReq         optionCode = 55
	optMessage          optionCode = 56
	optClientID         optionCode = 61
)

func (code optionCode) lenValid(l int) bool {
	switch code {
	case optSubnetMask, optDefaultGateway,
		optReqIPAddr, optLeaseTime, optDHCPServer:
		return l == 4
	case optDHCPMsgType:
		return l == 1
	case optDomainNameServer:
		return l%4 == 0
	case optMessage, optDomainName, optClientID:
		return l >= 1
	case optParamReq:
		return true // no fixed length
	default:
		return true // unknown option, assume ok
	}
}

type options []option

func (opts options) dhcpMsgType() (dhcpMsgType, error) {
	for _, opt := range opts {
		if opt.code == optDHCPMsgType {
			if len(opt.body) != 1 {
				return 0, fmt.Errorf("%s: bad length: %d", opt.code, len(opt.body))
			}
			v := opt.body[0]
			if v <= 0 || v >= 8 {
				return 0, fmt.Errorf("DHCP bad length: %d", len(opt.body))
			}
			return dhcpMsgType(v), nil
		}
	}
	return 0, nil
}

func (opts options) message() string {
	for _, opt := range opts {
		if opt.code == optMessage {
			return string(opt.body)
		}
	}
	return ""
}

func (opts options) len() int {
	l := 0
	for _, opt := range opts {
		l += 1 + 1 + len(opt.body) // code + len + body
	}
	return l + 1 // extra byte for 'pad' option
}

type op byte

const (
	opRequest op = 0x01
	opReply   op = 0x02
)

// dhcpMsgType is the DHCP Message Type from RFC 1533, section 9.4.
type dhcpMsgType byte

const (
	dhcpDISCOVER dhcpMsgType = 1
	dhcpOFFER    dhcpMsgType = 2
	dhcpREQUEST  dhcpMsgType = 3
	dhcpDECLINE  dhcpMsgType = 4
	dhcpACK      dhcpMsgType = 5
	dhcpNAK      dhcpMsgType = 6
	dhcpRELEASE  dhcpMsgType = 7
)
<|endoftext|>


<commit_before>// Copyright 2016-2017 Authors of Cilium
//
// 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 policy

import (
	"encoding/json"
	"fmt"
	"strconv"
	"strings"

	"github.com/cilium/cilium/api/v1/models"
	"github.com/cilium/cilium/pkg/policy/api"
)

type AuxRule struct {
	Expr string `json:"expr"`
}

type L4Filter struct {
	// Port is the destination port to allow
	Port int `json:"port,omitempty"`
	// Protocol is the L4 protocol to allow or NONE
	Protocol string `json:"protocol,omitempty"`
	// L7Parser specifies the L7 protocol parser (optional)
	L7Parser string `json:"l7-parser,omitempty"`
	// L7RedirectPort is the L7 proxy port to redirect to (optional)
	L7RedirectPort int `json:"l7-redirect-port,omitempty"`
	// L7Rules is a list of L7 rules which are passed to the L7 proxy (optional)
	L7Rules []AuxRule `json:"l7-rules,omitempty"`
}

// CreateL4Filter creates an L4Filter based on an api.PortRule and api.PortProtocol
func CreateL4Filter(rule api.PortRule, port api.PortProtocol, protocol string) L4Filter {
	// already validated via PortRule.Validate()
	p, _ := strconv.ParseUint(port.Port, 0, 16)

	l4 := L4Filter{
		Port:           int(p),
		Protocol:       protocol,
		L7RedirectPort: rule.RedirectPort,
	}

	if rule.Rules != nil {
		l7rules := []AuxRule{}
		for _, h := range rule.Rules.HTTP {
			r := AuxRule{}

			if h.Path != "" {
				r.Expr = "PathRegexp(\"" + h.Path + "\")"
			}

			if h.Method != "" {
				if r.Expr != "" {
					r.Expr += " && "
				}
				r.Expr += "MethodRegexp(\"" + h.Method + "\")"
			}

			if h.Host != "" {
				if r.Expr != "" {
					r.Expr += " && "
				}
				r.Expr += "HostRegexp(\"" + h.Host + "\")"
			}

			for _, hdr := range h.Headers {
				s := strings.SplitN(hdr, " ", 2)
				if r.Expr != "" {
					r.Expr += " && "
				}
				r.Expr += "Header(\""
				if len(s) == 2 {
					// Remove ':' in "X-Key: true"
					key := strings.TrimRight(s[0], ":")
					r.Expr += key + "\",\"" + s[1]
				} else {
					r.Expr += s[0]
				}
				r.Expr += "\")"
			}

			if r.Expr != "" {
				l7rules = append(l7rules, r)
			}
		}

		if len(l7rules) > 0 {
			l4.L7Parser = "http"
			l4.L7Rules = l7rules
		}
	}

	return l4
}

// IsRedirect returns true if the L4 filter contains a port redirection
func (l4 *L4Filter) IsRedirect() bool {
	return l4.L7Parser != ""
}

// MarshalIndent returns the `L4Filter` in indented JSON string.
func (l4 *L4Filter) MarshalIndent() string {
	b, err := json.MarshalIndent(l4, "", "  ")
	if err != nil {
		return err.Error()
	}
	return string(b)
}

// String returns the `L4Filter` in a human-readable string.
func (l4 L4Filter) String() string {
	b, err := json.Marshal(l4)
	if err != nil {
		return err.Error()
	}
	return string(b)
}

// L4PolicyMap is a list of L4 filters indexable by protocol/port
// key format: "port/proto"
type L4PolicyMap map[string]L4Filter

// HasRedirect returns true if at least one L4 filter contains a port
// redirection
func (l4 L4PolicyMap) HasRedirect() bool {
	for _, f := range l4 {
		if f.IsRedirect() {
			return true
		}
	}

	return false
}

// containsAllL4 checks if the L4PolicyMap contains all `l4Ports`. Returns false
// if the `L4PolicyMap` has a single rule and l4Ports is empty or if a single
// `l4Port`'s port is not present in the `L4PolicyMap`.
func (l4 L4PolicyMap) containsAllL4(l4Ports []*models.Port) api.Decision {
	if len(l4) == 0 {
		return api.Allowed
	}

	if len(l4Ports) == 0 {
		return api.Denied
	}

	for _, l4CtxIng := range l4Ports {
		lwrProtocol := strings.ToLower(l4CtxIng.Protocol)
		switch lwrProtocol {
		case "", models.PortProtocolAny:
			tcpPort := fmt.Sprintf("%d/tcp", l4CtxIng.Port)
			_, tcpmatch := l4[tcpPort]
			udpPort := fmt.Sprintf("%d/udp", l4CtxIng.Port)
			_, udpmatch := l4[udpPort]
			if !tcpmatch && !udpmatch {
				return api.Denied
			}
		default:
			port := fmt.Sprintf("%s/%d", lwrProtocol, l4CtxIng.Port)
			if _, match := l4[port]; !match {
				return api.Denied
			}
		}
	}
	return api.Allowed
}

type L4Policy struct {
	Ingress L4PolicyMap
	Egress  L4PolicyMap
}

func NewL4Policy() *L4Policy {
	return &L4Policy{
		Ingress: make(L4PolicyMap),
		Egress:  make(L4PolicyMap),
	}
}

// IngressCoversDPorts checks if the receiver's ingress `L4Policy` contains all
// `dPorts`.
func (l4 *L4Policy) IngressCoversDPorts(dPorts []*models.Port) api.Decision {
	return l4.Ingress.containsAllL4(dPorts)
}

// EgressCoversDPorts checks if the receiver's egress `L4Policy` contains all
// `dPorts`.
func (l4 *L4Policy) EgressCoversDPorts(dPorts []*models.Port) api.Decision {
	return l4.Egress.containsAllL4(dPorts)
}

// HasRedirect returns true if the L4 policy contains at least one port redirection
func (l4 *L4Policy) HasRedirect() bool {
	return l4 != nil && (l4.Ingress.HasRedirect() || l4.Egress.HasRedirect())
}

// RequiresConntrack returns true if if the L4 configuration requires
// connection tracking to be enabled.
func (l4 *L4Policy) RequiresConntrack() bool {
	return l4 != nil && (len(l4.Ingress) > 0 || len(l4.Egress) > 0)
}

func (l4 *L4Policy) GetModel() *models.L4Policy {
	if l4 == nil {
		return nil
	}

	ingress := []string{}
	for _, v := range l4.Ingress {
		ingress = append(ingress, v.MarshalIndent())
	}

	egress := []string{}
	for _, v := range l4.Egress {
		egress = append(egress, v.MarshalIndent())
	}

	return &models.L4Policy{
		Ingress: ingress,
		Egress:  egress,
	}
}

func (l4 *L4Policy) DeepCopy() *L4Policy {
	cpy := &L4Policy{
		Ingress: make(map[string]L4Filter, len(l4.Ingress)),
		Egress:  make(map[string]L4Filter, len(l4.Ingress)),
	}

	for k, v := range l4.Ingress {
		cpy.Ingress[k] = v
	}

	for k, v := range l4.Egress {
		cpy.Egress[k] = v
	}

	return cpy
}
<commit_msg>pkg/policy/l4: Fix DeepCopy.<commit_after>// Copyright 2016-2017 Authors of Cilium
//
// 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 policy

import (
	"encoding/json"
	"fmt"
	"strconv"
	"strings"

	"github.com/cilium/cilium/api/v1/models"
	"github.com/cilium/cilium/pkg/policy/api"
)

type AuxRule struct {
	Expr string `json:"expr"`
}

type L4Filter struct {
	// Port is the destination port to allow
	Port int `json:"port,omitempty"`
	// Protocol is the L4 protocol to allow or NONE
	Protocol string `json:"protocol,omitempty"`
	// L7Parser specifies the L7 protocol parser (optional)
	L7Parser string `json:"l7-parser,omitempty"`
	// L7RedirectPort is the L7 proxy port to redirect to (optional)
	L7RedirectPort int `json:"l7-redirect-port,omitempty"`
	// L7Rules is a list of L7 rules which are passed to the L7 proxy (optional)
	L7Rules []AuxRule `json:"l7-rules,omitempty"`
}

// CreateL4Filter creates an L4Filter based on an api.PortRule and api.PortProtocol
func CreateL4Filter(rule api.PortRule, port api.PortProtocol, protocol string) L4Filter {
	// already validated via PortRule.Validate()
	p, _ := strconv.ParseUint(port.Port, 0, 16)

	l4 := L4Filter{
		Port:           int(p),
		Protocol:       protocol,
		L7RedirectPort: rule.RedirectPort,
	}

	if rule.Rules != nil {
		l7rules := []AuxRule{}
		for _, h := range rule.Rules.HTTP {
			r := AuxRule{}

			if h.Path != "" {
				r.Expr = "PathRegexp(\"" + h.Path + "\")"
			}

			if h.Method != "" {
				if r.Expr != "" {
					r.Expr += " && "
				}
				r.Expr += "MethodRegexp(\"" + h.Method + "\")"
			}

			if h.Host != "" {
				if r.Expr != "" {
					r.Expr += " && "
				}
				r.Expr += "HostRegexp(\"" + h.Host + "\")"
			}

			for _, hdr := range h.Headers {
				s := strings.SplitN(hdr, " ", 2)
				if r.Expr != "" {
					r.Expr += " && "
				}
				r.Expr += "Header(\""
				if len(s) == 2 {
					// Remove ':' in "X-Key: true"
					key := strings.TrimRight(s[0], ":")
					r.Expr += key + "\",\"" + s[1]
				} else {
					r.Expr += s[0]
				}
				r.Expr += "\")"
			}

			if r.Expr != "" {
				l7rules = append(l7rules, r)
			}
		}

		if len(l7rules) > 0 {
			l4.L7Parser = "http"
			l4.L7Rules = l7rules
		}
	}

	return l4
}

// IsRedirect returns true if the L4 filter contains a port redirection
func (l4 *L4Filter) IsRedirect() bool {
	return l4.L7Parser != ""
}

// MarshalIndent returns the `L4Filter` in indented JSON string.
func (l4 *L4Filter) MarshalIndent() string {
	b, err := json.MarshalIndent(l4, "", "  ")
	if err != nil {
		return err.Error()
	}
	return string(b)
}

// String returns the `L4Filter` in a human-readable string.
func (l4 L4Filter) String() string {
	b, err := json.Marshal(l4)
	if err != nil {
		return err.Error()
	}
	return string(b)
}

// L4PolicyMap is a list of L4 filters indexable by protocol/port
// key format: "port/proto"
type L4PolicyMap map[string]L4Filter

// HasRedirect returns true if at least one L4 filter contains a port
// redirection
func (l4 L4PolicyMap) HasRedirect() bool {
	for _, f := range l4 {
		if f.IsRedirect() {
			return true
		}
	}

	return false
}

// containsAllL4 checks if the L4PolicyMap contains all `l4Ports`. Returns false
// if the `L4PolicyMap` has a single rule and l4Ports is empty or if a single
// `l4Port`'s port is not present in the `L4PolicyMap`.
func (l4 L4PolicyMap) containsAllL4(l4Ports []*models.Port) api.Decision {
	if len(l4) == 0 {
		return api.Allowed
	}

	if len(l4Ports) == 0 {
		return api.Denied
	}

	for _, l4CtxIng := range l4Ports {
		lwrProtocol := strings.ToLower(l4CtxIng.Protocol)
		switch lwrProtocol {
		case "", models.PortProtocolAny:
			tcpPort := fmt.Sprintf("%d/tcp", l4CtxIng.Port)
			_, tcpmatch := l4[tcpPort]
			udpPort := fmt.Sprintf("%d/udp", l4CtxIng.Port)
			_, udpmatch := l4[udpPort]
			if !tcpmatch && !udpmatch {
				return api.Denied
			}
		default:
			port := fmt.Sprintf("%s/%d", lwrProtocol, l4CtxIng.Port)
			if _, match := l4[port]; !match {
				return api.Denied
			}
		}
	}
	return api.Allowed
}

type L4Policy struct {
	Ingress L4PolicyMap
	Egress  L4PolicyMap
}

func NewL4Policy() *L4Policy {
	return &L4Policy{
		Ingress: make(L4PolicyMap),
		Egress:  make(L4PolicyMap),
	}
}

// IngressCoversDPorts checks if the receiver's ingress `L4Policy` contains all
// `dPorts`.
func (l4 *L4Policy) IngressCoversDPorts(dPorts []*models.Port) api.Decision {
	return l4.Ingress.containsAllL4(dPorts)
}

// EgressCoversDPorts checks if the receiver's egress `L4Policy` contains all
// `dPorts`.
func (l4 *L4Policy) EgressCoversDPorts(dPorts []*models.Port) api.Decision {
	return l4.Egress.containsAllL4(dPorts)
}

// HasRedirect returns true if the L4 policy contains at least one port redirection
func (l4 *L4Policy) HasRedirect() bool {
	return l4 != nil && (l4.Ingress.HasRedirect() || l4.Egress.HasRedirect())
}

// RequiresConntrack returns true if if the L4 configuration requires
// connection tracking to be enabled.
func (l4 *L4Policy) RequiresConntrack() bool {
	return l4 != nil && (len(l4.Ingress) > 0 || len(l4.Egress) > 0)
}

func (l4 *L4Policy) GetModel() *models.L4Policy {
	if l4 == nil {
		return nil
	}

	ingress := []string{}
	for _, v := range l4.Ingress {
		ingress = append(ingress, v.MarshalIndent())
	}

	egress := []string{}
	for _, v := range l4.Egress {
		egress = append(egress, v.MarshalIndent())
	}

	return &models.L4Policy{
		Ingress: ingress,
		Egress:  egress,
	}
}

func (l4 *L4Policy) DeepCopy() *L4Policy {
	cpy := &L4Policy{
		Ingress: make(L4PolicyMap, len(l4.Ingress)),
		Egress:  make(L4PolicyMap, len(l4.Egress)),
	}

	for k, v := range l4.Ingress {
		cpy.Ingress[k] = v
	}

	for k, v := range l4.Egress {
		cpy.Egress[k] = v
	}

	return cpy
}
<|endoftext|>


<commit_before>package filter

import (
	"fmt"
	"path/filepath"
	"strings"

	"github.com/reviewdog/reviewdog/diff"
	"github.com/reviewdog/reviewdog/service/serviceutil"
)

// Mode represents enumeration of available filter modes
type Mode int

const (
	// ModeDefault represents default mode, which means users doesn't specify
	// filter-mode. The behavior can be changed depending on reporters/context
	// later if we want. Basically, it's same as ModeAdded because it's most safe
	// and basic mode for reporters implementation.
	ModeDefault Mode = iota
	// ModeAdded represents filtering by added/changed diff lines.
	ModeAdded
	// ModeDiffContext represents filtering by diff context.
	// i.e. changed lines +-N lines (e.g. N=3 for default git diff).
	ModeDiffContext
	// ModeFile represents filtering by changed files.
	ModeFile
	// ModeNoFilter doesn't filter out any results.
	ModeNoFilter
)

// String implements the flag.Value interface
func (mode *Mode) String() string {
	names := [...]string{
		"default",
		"added",
		"diff_context",
		"file",
		"nofilter",
	}
	if *mode < ModeDefault || *mode > ModeNoFilter {
		return "Unknown mode"
	}

	return names[*mode]
}

// Set implements the flag.Value interface
func (mode *Mode) Set(value string) error {
	switch value {
	case "default", "":
		*mode = ModeDefault
	case "added":
		*mode = ModeAdded
	case "diff_context":
		*mode = ModeDiffContext
	case "file":
		*mode = ModeFile
	case "nofilter":
		*mode = ModeNoFilter
	default:
		return fmt.Errorf("invalid mode name: %s", value)
	}
	return nil
}

// DiffFilter filters lines by diff.
type DiffFilter struct {
	// Current working directory (workdir).
	cwd string

	// Relative path to the project root (e.g. git) directory from current workdir.
	// It can be empty if it doesn't find any project root directory.
	projectRelPath string

	strip int
	mode  Mode

	difflines difflines
	difffiles difffiles
}

// difflines is a hash table of normalizedPath to line number to *diff.Line.
type difflines map[normalizedPath]map[int]*diff.Line

// difffiles is a hash table of normalizedPath to *diff.FileDiff.
type difffiles map[normalizedPath]*diff.FileDiff

// NewDiffFilter creates a new DiffFilter.
func NewDiffFilter(diff []*diff.FileDiff, strip int, cwd string, mode Mode) *DiffFilter {
	df := &DiffFilter{
		strip:     strip,
		cwd:       cwd,
		mode:      mode,
		difflines: make(difflines),
		difffiles: make(difffiles),
	}
	// If cwd is empty, projectRelPath should not have any meaningful data too.
	if cwd != "" {
		df.projectRelPath, _ = serviceutil.GitRelWorkdir()
	}
	df.addDiff(diff)
	return df
}

func (df *DiffFilter) addDiff(filediffs []*diff.FileDiff) {
	for _, filediff := range filediffs {
		path := df.normalizeDiffPath(filediff)
		df.difffiles[path] = filediff
		lines, ok := df.difflines[path]
		if !ok {
			lines = make(map[int]*diff.Line)
		}
		for _, hunk := range filediff.Hunks {
			for _, line := range hunk.Lines {
				if line.LnumNew > 0 {
					lines[line.LnumNew] = line
				}
			}
		}
		df.difflines[path] = lines
	}
}

// ShouldReport returns true, if the given path should be reported depending on
// the filter Mode. It also optionally return diff file/line.
func (df *DiffFilter) ShouldReport(path string, lnum int) (bool, *diff.FileDiff, *diff.Line) {
	npath := df.normalizePath(path)
	file := df.difffiles[npath]
	lines, ok := df.difflines[npath]
	if !ok {
		return (df.mode == ModeNoFilter), file, nil
	}
	line, ok := lines[lnum]
	if !ok {
		return (df.mode == ModeNoFilter || df.mode == ModeFile), file, nil
	}
	return df.isSignificantLine(line), file, line
}

func (df *DiffFilter) isSignificantLine(line *diff.Line) bool {
	switch df.mode {
	case ModeDiffContext, ModeFile, ModeNoFilter:
		return true // any lines in diff are significant.
	case ModeAdded, ModeDefault:
		return line.Type == diff.LineAdded
	}
	return false
}

// normalizedPath is file path which is relative to **project root dir** or
// to current dir if project root not found.
type normalizedPath struct{ p string }

func (df *DiffFilter) normalizePath(path string) normalizedPath {
	return normalizedPath{p: NormalizePath(path, df.cwd, df.projectRelPath)}
}

func contains(path, base string) bool {
	ps := splitPathList(path)
	bs := splitPathList(base)
	if len(ps) < len(bs) {
		return false
	}
	for i := range bs {
		if bs[i] != ps[i] {
			return false
		}
	}
	return true
}

// Assuming diff path should be relative path to the project root dir by
// default (e.g. git diff).
//
// `git diff --relative` can returns relative path to current workdir, so we
// ask users not to use it for reviewdog command.
func (df *DiffFilter) normalizeDiffPath(filediff *diff.FileDiff) normalizedPath {
	return normalizedPath{p: NormalizeDiffPath(filediff.PathNew, df.strip)}
}

// NormalizeDiffPath return path normalized path from given path in diff with
// strip.
func NormalizeDiffPath(diffpath string, strip int) string {
	if diffpath == "/dev/null" {
		return ""
	}
	path := diffpath
	if strip > 0 {
		ps := splitPathList(path)
		if len(ps) > strip {
			path = filepath.Join(ps[strip:]...)
		}
	}
	return filepath.ToSlash(filepath.Clean(path))
}

func splitPathList(path string) []string {
	return strings.Split(filepath.ToSlash(path), "/")
}
<commit_msg>filter: do not strip when path is absolute<commit_after>package filter

import (
	"fmt"
	"path/filepath"
	"strings"

	"github.com/reviewdog/reviewdog/diff"
	"github.com/reviewdog/reviewdog/service/serviceutil"
)

// Mode represents enumeration of available filter modes
type Mode int

const (
	// ModeDefault represents default mode, which means users doesn't specify
	// filter-mode. The behavior can be changed depending on reporters/context
	// later if we want. Basically, it's same as ModeAdded because it's most safe
	// and basic mode for reporters implementation.
	ModeDefault Mode = iota
	// ModeAdded represents filtering by added/changed diff lines.
	ModeAdded
	// ModeDiffContext represents filtering by diff context.
	// i.e. changed lines +-N lines (e.g. N=3 for default git diff).
	ModeDiffContext
	// ModeFile represents filtering by changed files.
	ModeFile
	// ModeNoFilter doesn't filter out any results.
	ModeNoFilter
)

// String implements the flag.Value interface
func (mode *Mode) String() string {
	names := [...]string{
		"default",
		"added",
		"diff_context",
		"file",
		"nofilter",
	}
	if *mode < ModeDefault || *mode > ModeNoFilter {
		return "Unknown mode"
	}

	return names[*mode]
}

// Set implements the flag.Value interface
func (mode *Mode) Set(value string) error {
	switch value {
	case "default", "":
		*mode = ModeDefault
	case "added":
		*mode = ModeAdded
	case "diff_context":
		*mode = ModeDiffContext
	case "file":
		*mode = ModeFile
	case "nofilter":
		*mode = ModeNoFilter
	default:
		return fmt.Errorf("invalid mode name: %s", value)
	}
	return nil
}

// DiffFilter filters lines by diff.
type DiffFilter struct {
	// Current working directory (workdir).
	cwd string

	// Relative path to the project root (e.g. git) directory from current workdir.
	// It can be empty if it doesn't find any project root directory.
	projectRelPath string

	strip int
	mode  Mode

	difflines difflines
	difffiles difffiles
}

// difflines is a hash table of normalizedPath to line number to *diff.Line.
type difflines map[normalizedPath]map[int]*diff.Line

// difffiles is a hash table of normalizedPath to *diff.FileDiff.
type difffiles map[normalizedPath]*diff.FileDiff

// NewDiffFilter creates a new DiffFilter.
func NewDiffFilter(diff []*diff.FileDiff, strip int, cwd string, mode Mode) *DiffFilter {
	df := &DiffFilter{
		strip:     strip,
		cwd:       cwd,
		mode:      mode,
		difflines: make(difflines),
		difffiles: make(difffiles),
	}
	// If cwd is empty, projectRelPath should not have any meaningful data too.
	if cwd != "" {
		df.projectRelPath, _ = serviceutil.GitRelWorkdir()
	}
	df.addDiff(diff)
	return df
}

func (df *DiffFilter) addDiff(filediffs []*diff.FileDiff) {
	for _, filediff := range filediffs {
		path := df.normalizeDiffPath(filediff)
		df.difffiles[path] = filediff
		lines, ok := df.difflines[path]
		if !ok {
			lines = make(map[int]*diff.Line)
		}
		for _, hunk := range filediff.Hunks {
			for _, line := range hunk.Lines {
				if line.LnumNew > 0 {
					lines[line.LnumNew] = line
				}
			}
		}
		df.difflines[path] = lines
	}
}

// ShouldReport returns true, if the given path should be reported depending on
// the filter Mode. It also optionally return diff file/line.
func (df *DiffFilter) ShouldReport(path string, lnum int) (bool, *diff.FileDiff, *diff.Line) {
	npath := df.normalizePath(path)
	file := df.difffiles[npath]
	lines, ok := df.difflines[npath]
	if !ok {
		return (df.mode == ModeNoFilter), file, nil
	}
	line, ok := lines[lnum]
	if !ok {
		return (df.mode == ModeNoFilter || df.mode == ModeFile), file, nil
	}
	return df.isSignificantLine(line), file, line
}

func (df *DiffFilter) isSignificantLine(line *diff.Line) bool {
	switch df.mode {
	case ModeDiffContext, ModeFile, ModeNoFilter:
		return true // any lines in diff are significant.
	case ModeAdded, ModeDefault:
		return line.Type == diff.LineAdded
	}
	return false
}

// normalizedPath is file path which is relative to **project root dir** or
// to current dir if project root not found.
type normalizedPath struct{ p string }

func (df *DiffFilter) normalizePath(path string) normalizedPath {
	return normalizedPath{p: NormalizePath(path, df.cwd, df.projectRelPath)}
}

func contains(path, base string) bool {
	ps := splitPathList(path)
	bs := splitPathList(base)
	if len(ps) < len(bs) {
		return false
	}
	for i := range bs {
		if bs[i] != ps[i] {
			return false
		}
	}
	return true
}

// Assuming diff path should be relative path to the project root dir by
// default (e.g. git diff).
//
// `git diff --relative` can returns relative path to current workdir, so we
// ask users not to use it for reviewdog command.
func (df *DiffFilter) normalizeDiffPath(filediff *diff.FileDiff) normalizedPath {
	return normalizedPath{p: NormalizeDiffPath(filediff.PathNew, df.strip)}
}

// NormalizeDiffPath return path normalized path from given path in diff with
// strip.
func NormalizeDiffPath(diffpath string, strip int) string {
	if diffpath == "/dev/null" {
		return ""
	}
	path := diffpath
	if strip > 0 && !filepath.IsAbs(path) {
		ps := splitPathList(path)
		if len(ps) > strip {
			path = filepath.Join(ps[strip:]...)
		}
	}
	return filepath.ToSlash(filepath.Clean(path))
}

func splitPathList(path string) []string {
	return strings.Split(filepath.ToSlash(path), "/")
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"sync"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/aws/aws-sdk-go/service/rds"
	"github.com/mitchellh/cli"
)

type RRCommand struct {
	header     bool
	printEmpty bool
	account    string
	Ui         cli.Ui
}

// Help function displays detailed help for ths reserver-report sub command
func (c *RRCommand) Help() string {
	return `
	Description:
	Produce CSV output with details of all active EC2 & RDS reserved instances

	Usage:
		awsgo-tools reserved-report [flags]

	Flags:
	-a <account name> - account name to use in CSV output
	-e - produce an empty line if no reserved instances found
	-h - print headers and exit
	`
}

// Synopsis function returns a string with concise details of the sub command
func (c *RRCommand) Synopsis() string {
	return "EC2 & RDS reserved Instance report CSV Output"
}

// Run function is the function called by the cli library to run the actual sub command code.
func (c *RRCommand) Run(args []string) int {

	cmdFlags := flag.NewFlagSet("reserved-report", flag.ContinueOnError)
	cmdFlags.Usage = func() { c.Ui.Output(c.Help()) }

	cmdFlags.BoolVar(&c.header, "h", false, "Produce CSV Headers and exit")
	cmdFlags.BoolVar(&c.printEmpty, "e", false, "Print empty line if no reserved instances found")
	cmdFlags.StringVar(&c.account, "a", "unknown", "AWS Account Name to use")
	if err := cmdFlags.Parse(args); err != nil {
		fmt.Printf("Error processing commandline flags\n")
		return RCERR
	}

	if c.header {
		fmt.Printf("Account Name, State, Reservation Type, Expiry Date, Item Count, AV Zone, Instance Type, Offering Type, Reserved Instance ID \n")
		return RCOK
	}

	var wg sync.WaitGroup
	var ec2resp *ec2.DescribeReservedInstancesOutput
	var rdsresp *rds.DescribeReservedDBInstancesOutput
	var ec2err, rdserr error

	// use concurrency to ask AWS multiple questions at once
	wg.Add(1)
	go func() {

		defer wg.Done()
		// setup ec2 filter
		ec2Filter := ec2.Filter{}
		ec2Filter.Name = aws.String("state")
		ec2Filter.Values = []*string{aws.String("active")}
		ec2drii := ec2.DescribeReservedInstancesInput{Filters: []*ec2.Filter{&ec2Filter}}

		// Create an EC2 service object
		// Config details Keys, secret keys and region will be read from environment
		ec2svc := ec2.New(&aws.Config{MaxRetries: 10})

		// Call the DescribeInstances Operation
		ec2resp, ec2err = ec2svc.DescribeReservedInstances(&ec2drii)

	}()

	wg.Add(1)
	go func() {

		defer wg.Done()
		// Config details Keys, secret keys and region will be read from environment
		rdssvc := rds.New(&aws.Config{MaxRetries: 10})

		// Call the DescribeInstances Operation. Note Filters are not currently supported
		rdsresp, rdserr := rdssvc.DescribeReservedDBInstances(nil)

	}()

	// wait until both goroutines have completed talking to AWS
	wg.Wait()

	if ec2err != nil {
		fmt.Printf("AWS error: %s\n", ec2err)
		return RCERR
	}

	if rdserr != nil {
		fmt.Printf("AWS error: %s\n", rdserr)
		return RCERR
	}

	// extract the reserved instance details for ec2
	for _, ri := range ec2resp.ReservedInstances {

		// compute the expiry date from start + duration
		endDate := ri.Start.Add(time.Duration(*ri.Duration) * time.Second)

		fmt.Printf("%s,%s,%s,%s,%d,%s,%s,%s,%s\n",
			safeString(&c.account),
			safeString(ri.State),
			safeString(aws.String("ec2")),
			fmt.Sprintf("%d-%d-%d", endDate.Year(), endDate.Month(), endDate.Day()),
			*ri.InstanceCount,
			safeString(ri.AvailabilityZone),
			safeString(ri.InstanceType),
			safeString(ri.OfferingType),
			safeString(ri.ReservedInstancesID))

	}

	// extract the rds reserved instance details for rds
	for _, ri := range rdsresp.ReservedDBInstances {

		// rds does not currently support filters so need to filter at the output end
		if *ri.State != "active" {
			continue
		}

		// compute the expiry date from start + duration
		endDate := ri.StartTime.Add(time.Duration(*ri.Duration) * time.Second)

		var avZone string
		if *ri.MultiAZ {
			avZone = "Multi Zone"
		} else {
			avZone = "Single Zone"
		}

		fmt.Printf("%s,%s,%s,%s,%d,%s,%s,%s,%s\n",
			safeString(&c.account),
			safeString(ri.State),
			safeString(aws.String("rds")),
			fmt.Sprintf("%d-%d-%d", endDate.Year(), endDate.Month(), endDate.Day()),
			*ri.DBInstanceCount,
			avZone,
			safeString(ri.DBInstanceClass),
			safeString(ri.OfferingType),
			safeString(ri.ReservedDBInstanceID))

	}

	if c.printEmpty && (len(ec2resp.ReservedInstances)+len(rdsresp.ReservedDBInstances)) == 0 {
		fmt.Printf("%s,,,,,,,,\n", c.account)
	}

	return RCOK
}

/*

 */
<commit_msg>bug fix in reserved report<commit_after>package main

import (
	"flag"
	"fmt"
	"sync"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/aws/aws-sdk-go/service/rds"
	"github.com/mitchellh/cli"
)

type RRCommand struct {
	header     bool
	printEmpty bool
	account    string
	Ui         cli.Ui
}

// Help function displays detailed help for ths reserver-report sub command
func (c *RRCommand) Help() string {
	return `
	Description:
	Produce CSV output with details of all active EC2 & RDS reserved instances

	Usage:
		awsgo-tools reserved-report [flags]

	Flags:
	-a <account name> - account name to use in CSV output
	-e - produce an empty line if no reserved instances found
	-h - print headers and exit
	`
}

// Synopsis function returns a string with concise details of the sub command
func (c *RRCommand) Synopsis() string {
	return "EC2 & RDS reserved Instance report CSV Output"
}

// Run function is the function called by the cli library to run the actual sub command code.
func (c *RRCommand) Run(args []string) int {

	cmdFlags := flag.NewFlagSet("reserved-report", flag.ContinueOnError)
	cmdFlags.Usage = func() { c.Ui.Output(c.Help()) }

	cmdFlags.BoolVar(&c.header, "h", false, "Produce CSV Headers and exit")
	cmdFlags.BoolVar(&c.printEmpty, "e", false, "Print empty line if no reserved instances found")
	cmdFlags.StringVar(&c.account, "a", "unknown", "AWS Account Name to use")
	if err := cmdFlags.Parse(args); err != nil {
		fmt.Printf("Error processing commandline flags\n")
		return RCERR
	}

	if c.header {
		fmt.Printf("Account Name, State, Reservation Type, Expiry Date, Item Count, AV Zone, Instance Type, Offering Type, Reserved Instance ID \n")
		return RCOK
	}

	var wg sync.WaitGroup
	var ec2resp *ec2.DescribeReservedInstancesOutput
	var rdsresp *rds.DescribeReservedDBInstancesOutput
	var ec2err, rdserr error

	// use concurrency to ask AWS multiple questions at once
	wg.Add(1)
	go func() {

		defer wg.Done()
		// setup ec2 filter
		ec2Filter := ec2.Filter{}
		ec2Filter.Name = aws.String("state")
		ec2Filter.Values = []*string{aws.String("active")}
		ec2drii := ec2.DescribeReservedInstancesInput{Filters: []*ec2.Filter{&ec2Filter}}

		// Create an EC2 service object
		// Config details Keys, secret keys and region will be read from environment
		ec2svc := ec2.New(&aws.Config{MaxRetries: 10})

		// Call the DescribeInstances Operation
		ec2resp, ec2err = ec2svc.DescribeReservedInstances(&ec2drii)

	}()

	wg.Add(1)
	go func() {

		defer wg.Done()
		// Config details Keys, secret keys and region will be read from environment
		rdssvc := rds.New(&aws.Config{MaxRetries: 10})

		// Call the DescribeInstances Operation. Note Filters are not currently supported
		rdsresp, rdserr = rdssvc.DescribeReservedDBInstances(nil)

	}()

	// wait until both goroutines have completed talking to AWS
	wg.Wait()

	if ec2err != nil {
		fmt.Printf("AWS error: %s\n", ec2err)
		return RCERR
	}

	if rdserr != nil {
		fmt.Printf("AWS error: %s\n", rdserr)
		return RCERR
	}

	// extract the reserved instance details for ec2
	for _, ri := range ec2resp.ReservedInstances {

		// compute the expiry date from start + duration
		endDate := ri.Start.Add(time.Duration(*ri.Duration) * time.Second)

		fmt.Printf("%s,%s,%s,%s,%d,%s,%s,%s,%s\n",
			safeString(&c.account),
			safeString(ri.State),
			safeString(aws.String("ec2")),
			fmt.Sprintf("%d-%d-%d", endDate.Year(), endDate.Month(), endDate.Day()),
			*ri.InstanceCount,
			safeString(ri.AvailabilityZone),
			safeString(ri.InstanceType),
			safeString(ri.OfferingType),
			safeString(ri.ReservedInstancesID))

	}

	// extract the rds reserved instance details for rds
	for _, ri := range rdsresp.ReservedDBInstances {

		// rds does not currently support filters so need to filter at the output end
		if *ri.State != "active" {
			continue
		}

		// compute the expiry date from start + duration
		endDate := ri.StartTime.Add(time.Duration(*ri.Duration) * time.Second)

		var avZone string
		if *ri.MultiAZ {
			avZone = "Multi Zone"
		} else {
			avZone = "Single Zone"
		}

		fmt.Printf("%s,%s,%s,%s,%d,%s,%s,%s,%s\n",
			safeString(&c.account),
			safeString(ri.State),
			safeString(aws.String("rds")),
			fmt.Sprintf("%d-%d-%d", endDate.Year(), endDate.Month(), endDate.Day()),
			*ri.DBInstanceCount,
			avZone,
			safeString(ri.DBInstanceClass),
			safeString(ri.OfferingType),
			safeString(ri.ReservedDBInstanceID))

	}

	if c.printEmpty && (len(ec2resp.ReservedInstances)+len(rdsresp.ReservedDBInstances)) == 0 {
		fmt.Printf("%s,,,,,,,,\n", c.account)
	}

	return RCOK
}

/*

 */
<|endoftext|>