<commit_before>package stackit

import (
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/cloudformation"
	"github.com/pkg/errors"
	"fmt"
	"time"
)

type StackitUpInput struct {
	RoleARN          string
	StackPolicyBody  string
	TemplateBody     string
	PreviousTemplate bool
	Parameters       []*cloudformation.Parameter
	Tags             map[string]string
	NotificationARNs []string
	PopulateMissing  bool
}

func (s *Stackit) populateMissing(input *StackitUpInput) error {
	stack, _ := s.Describe()

	maybeAddParam := func(name string) {
		for _, param := range input.Parameters {
			if *param.ParameterKey == name {
				return
			}
		}
		input.Parameters = append(input.Parameters, &cloudformation.Parameter{
			ParameterKey:     &name,
			UsePreviousValue: aws.Bool(true),
		})
	}

	if len(input.TemplateBody) == 0 {
		input.PreviousTemplate = true

		for _, param := range stack.Parameters {
			maybeAddParam(*param.ParameterKey)
		}
	} else {
		resp, err := s.api.ValidateTemplate(&cloudformation.ValidateTemplateInput{TemplateBody: &input.TemplateBody})
		if err != nil {
			return err
		}

		for _, param := range resp.Parameters {
			maybeAddParam(*param.ParameterKey)
		}
	}

	return nil
}

func (s *Stackit) EnsureStackReady(events chan<- TailStackEvent) error {
	stack, err := s.Describe()
	if err != nil {
		s.error(err, events)
		return err
	}

	cleanup := func() {
		token := generateToken()
		s.api.DeleteStack(&cloudformation.DeleteStackInput{StackName: &s.stackId, ClientRequestToken: &token})
		s.PollStackEvents(token, func(event TailStackEvent) {
			events <- event
		})
	}

	if stack != nil { // stack already exists
		if !IsTerminalStatus(*stack.StackStatus) && *stack.StackStatus != "REVIEW_IN_PROGRESS" {
			s.PollStackEvents("", func(event TailStackEvent) {
				events <- event
			})
		}

		stack, err = s.Describe()
		if err != nil {
			s.error(err, events)
			return err
		}

		if *stack.StackStatus == "CREATE_FAILED" || *stack.StackStatus == "ROLLBACK_COMPLETE" {
			cleanup()
		} else if *stack.StackStatus == "REVIEW_IN_PROGRESS" {
			resp, err := s.api.ListStackResources(&cloudformation.ListStackResourcesInput{StackName: &s.stackId})
			if err != nil {
				s.error(err, events)
				return err
			}
			if len(resp.StackResourceSummaries) == 0 {
				cleanup()
			}
		}
	}

	return nil
}

func (s *Stackit) Up(input StackitUpInput, events chan<- TailStackEvent) {
	s.stackId = ""
	stack, err := s.Describe()
	if err != nil {
		s.error(err, events)
	}

	token := generateToken()

	createInput := &cloudformation.CreateChangeSetInput{
		ChangeSetName:       aws.String(fmt.Sprintf("csid-%d", time.Now().Unix())),
		StackName:           &s.stackName,
		Capabilities:        aws.StringSlice([]string{"CAPABILITY_IAM", "CAPABILITY_NAMED_IAM"}),
		Parameters:          input.Parameters,
		Tags:                mapToTags(input.Tags),
		NotificationARNs:    aws.StringSlice(input.NotificationARNs),
		ClientToken:         &token,
		UsePreviousTemplate: &input.PreviousTemplate,
	}

	if len(input.TemplateBody) > 0 {
		createInput.TemplateBody = &input.TemplateBody
	}

	if len(input.RoleARN) > 0 {
		createInput.RoleARN = &input.RoleARN
	}

	if stack != nil { // stack already exists
		createInput.ChangeSetType = aws.String(cloudformation.ChangeSetTypeUpdate)

		if input.PopulateMissing {
			err := s.populateMissing(&input)
			if err != nil {
				s.error(errors.Wrap(err, "populating missing parameters"), events)
				return
			}
		}
	} else {
		createInput.ChangeSetType = aws.String(cloudformation.ChangeSetTypeCreate)
	}

	resp, err := s.api.CreateChangeSet(createInput)
	if err != nil {
		s.error(errors.Wrap(err, "creating change set"), events)
		return
	}

	s.stackId = *resp.StackId

	change, err := s.waitForChangeset(resp.Id)
	isNoop := change != nil && len(change.Changes) == 0

	if isNoop { // update is a no-op, nothing to change
		s.api.DeleteChangeSet(&cloudformation.DeleteChangeSetInput{ChangeSetName: resp.Id})
	}

	if err != nil {
		s.error(err, events)
		return
	} else if isNoop {
		close(events)
		return
	}

	_, err = s.api.ExecuteChangeSet(&cloudformation.ExecuteChangeSetInput{
		ChangeSetName:      resp.Id,
		ClientRequestToken: &token,
	})
	if err != nil {
		s.error(errors.Wrap(err, "executing change set"), events)
		return
	}

	s.PollStackEvents(token, func(event TailStackEvent) {
		events <- event
	})
	close(events)
}
<commit_msg>Fix population of missing parameters<commit_after>package stackit

import (
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/cloudformation"
	"github.com/pkg/errors"
	"fmt"
	"time"
)

type StackitUpInput struct {
	RoleARN          string
	StackPolicyBody  string
	TemplateBody     string
	PreviousTemplate bool
	Parameters       []*cloudformation.Parameter
	Tags             map[string]string
	NotificationARNs []string
	PopulateMissing  bool
}

func (s *Stackit) populateMissing(input *StackitUpInput) error {
	stack, _ := s.Describe()

	maybeAddParam := func(name string) {
		for _, param := range input.Parameters {
			if *param.ParameterKey == name {
				return
			}
		}
		input.Parameters = append(input.Parameters, &cloudformation.Parameter{
			ParameterKey:     &name,
			UsePreviousValue: aws.Bool(true),
		})
	}

	if len(input.TemplateBody) == 0 {
		input.PreviousTemplate = true

		for _, param := range stack.Parameters {
			maybeAddParam(*param.ParameterKey)
		}
	} else {
		resp, err := s.api.ValidateTemplate(&cloudformation.ValidateTemplateInput{TemplateBody: &input.TemplateBody})
		if err != nil {
			return err
		}

		for _, param := range resp.Parameters {
			maybeAddParam(*param.ParameterKey)
		}
	}

	return nil
}

func (s *Stackit) EnsureStackReady(events chan<- TailStackEvent) error {
	stack, err := s.Describe()
	if err != nil {
		s.error(err, events)
		return err
	}

	cleanup := func() {
		token := generateToken()
		s.api.DeleteStack(&cloudformation.DeleteStackInput{StackName: &s.stackId, ClientRequestToken: &token})
		s.PollStackEvents(token, func(event TailStackEvent) {
			events <- event
		})
	}

	if stack != nil { // stack already exists
		if !IsTerminalStatus(*stack.StackStatus) && *stack.StackStatus != "REVIEW_IN_PROGRESS" {
			s.PollStackEvents("", func(event TailStackEvent) {
				events <- event
			})
		}

		stack, err = s.Describe()
		if err != nil {
			s.error(err, events)
			return err
		}

		if *stack.StackStatus == "CREATE_FAILED" || *stack.StackStatus == "ROLLBACK_COMPLETE" {
			cleanup()
		} else if *stack.StackStatus == "REVIEW_IN_PROGRESS" {
			resp, err := s.api.ListStackResources(&cloudformation.ListStackResourcesInput{StackName: &s.stackId})
			if err != nil {
				s.error(err, events)
				return err
			}
			if len(resp.StackResourceSummaries) == 0 {
				cleanup()
			}
		}
	}

	return nil
}

func (s *Stackit) Up(input StackitUpInput, events chan<- TailStackEvent) {
	s.stackId = ""
	stack, err := s.Describe()
	if err != nil {
		s.error(err, events)
	}

	if input.PopulateMissing && stack != nil {
		err := s.populateMissing(&input)
		if err != nil {
			s.error(errors.Wrap(err, "populating missing parameters"), events)
			return
		}
	}

	token := generateToken()

	createInput := &cloudformation.CreateChangeSetInput{
		ChangeSetName:       aws.String(fmt.Sprintf("csid-%d", time.Now().Unix())),
		StackName:           &s.stackName,
		Capabilities:        aws.StringSlice([]string{"CAPABILITY_IAM", "CAPABILITY_NAMED_IAM"}),
		Parameters:          input.Parameters,
		Tags:                mapToTags(input.Tags),
		NotificationARNs:    aws.StringSlice(input.NotificationARNs),
		ClientToken:         &token,
		UsePreviousTemplate: &input.PreviousTemplate,
	}

	if len(input.TemplateBody) > 0 {
		createInput.TemplateBody = &input.TemplateBody
	}

	if len(input.RoleARN) > 0 {
		createInput.RoleARN = &input.RoleARN
	}

	if stack != nil { // stack already exists
		createInput.ChangeSetType = aws.String(cloudformation.ChangeSetTypeUpdate)
	} else {
		createInput.ChangeSetType = aws.String(cloudformation.ChangeSetTypeCreate)
	}

	resp, err := s.api.CreateChangeSet(createInput)
	if err != nil {
		s.error(errors.Wrap(err, "creating change set"), events)
		return
	}

	s.stackId = *resp.StackId

	change, err := s.waitForChangeset(resp.Id)
	isNoop := change != nil && len(change.Changes) == 0

	if isNoop { // update is a no-op, nothing to change
		s.api.DeleteChangeSet(&cloudformation.DeleteChangeSetInput{ChangeSetName: resp.Id})
	}

	if err != nil {
		s.error(err, events)
		return
	} else if isNoop {
		close(events)
		return
	}

	_, err = s.api.ExecuteChangeSet(&cloudformation.ExecuteChangeSetInput{
		ChangeSetName:      resp.Id,
		ClientRequestToken: &token,
	})
	if err != nil {
		s.error(errors.Wrap(err, "executing change set"), events)
		return
	}

	s.PollStackEvents(token, func(event TailStackEvent) {
		events <- event
	})
	close(events)
}
<|endoftext|>


<commit_before>// Copyright 2017 The hlin 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 store

import (
	pb "github.com/brancz/hlin/pkg/api/apipb"

	"github.com/coreos/etcd/clientv3"
	"github.com/go-kit/kit/log"
	proto "github.com/gogo/protobuf/proto"
	"github.com/pkg/errors"
	uuid "github.com/satori/go.uuid"
	"golang.org/x/net/context"
)

var SecretNotFound = errors.New("secret not found")
var SharesNotFound = errors.New("shares not found")
var CipherTextNotFound = errors.New("cipher text not found")

type SecretStore interface {
	CreateSecret(ctx context.Context, secretId string, s *pb.CreateSecretRequest) (*pb.PlainSecret, error)
	GetSecret(ctx context.Context, secretId string) (*pb.PlainSecret, error)
	GetPublicShares(ctx context.Context, secretId string) (*pb.PublicShares, error)
	GetPrivateShares(ctx context.Context, secretId, receiver string) (*pb.PrivateShares, error)
	GetCipherText(ctx context.Context, secretId string) (*pb.CipherText, error)
}

var WrongNumberOfResults = errors.New("incorrect number of results from etcd")

type EtcdStore struct {
	etcdclient *clientv3.Client
	logger     log.Logger
}

func NewEtcdStore(c *clientv3.Client, logger log.Logger) SecretStore {
	return &EtcdStore{
		etcdclient: c,
		logger:     logger,
	}
}

func (e *EtcdStore) CreateSecret(ctx context.Context, secretId string, s *pb.CreateSecretRequest) (*pb.PlainSecret, error) {
	ps := &pb.PlainSecret{SecretId: secretId}
	out, err := proto.Marshal(ps)
	if err != nil {
		return nil, errors.Wrap(err, "failed to encode PlainSecret")
	}

	_, err = e.etcdclient.Put(ctx, secretKey(secretId), string(out))
	if err != nil {
		return nil, errors.Wrap(err, "Secret could not be saved to etcd")
	}

	out, err = proto.Marshal(s.Shares.Public)
	if err != nil {
		return nil, errors.Wrap(err, "failed to encode PublicShare")
	}
	_, err = e.etcdclient.Put(ctx, publicSharesKey(secretId), string(out))
	if err != nil {
		return nil, errors.Wrap(err, "PublicShare could not be saved to etcd")
	}

	for _, share := range s.Shares.Private.Items {
		out, err = proto.Marshal(share)
		if err != nil {
			return nil, errors.Wrap(err, "failed to encode PublicShare")
		}
		_, err = e.etcdclient.Put(ctx, privateShareKey(secretId, share.Receiver, uuid.NewV4().String()), string(out))
		if err != nil {
			return nil, errors.Wrap(err, "PublicShare could not be saved to etcd")
		}
	}

	out, err = proto.Marshal(s.CipherText)
	if err != nil {
		return nil, errors.Wrap(err, "failed to encode CipherText")
	}
	_, err = e.etcdclient.Put(ctx, cipherTextKey(secretId), string(out))
	if err != nil {
		return nil, errors.Wrap(err, "CipherText could not be saved to etcd")
	}

	return ps, nil
}

func (e *EtcdStore) GetSecret(ctx context.Context, secretId string) (*pb.PlainSecret, error) {
	r, err := e.etcdclient.Get(ctx, secretKey(secretId))
	if err != nil {
		return nil, errors.Wrap(err, "getting secret from etcd failed")
	}

	if r.Count == 0 {
		return nil, SecretNotFound
	}
	if r.Count != 1 {
		return nil, WrongNumberOfResults
	}

	ps := &pb.PlainSecret{}
	err = proto.Unmarshal(r.Kvs[0].Value, ps)
	if err != nil {
		return nil, errors.Wrap(err, "unmarshaling PlainSecret failed")
	}

	return ps, nil
}

func (e *EtcdStore) GetPublicShares(ctx context.Context, secretId string) (*pb.PublicShares, error) {
	r, err := e.etcdclient.Get(ctx, publicSharesKey(secretId))
	if err != nil {
		return nil, errors.Wrap(err, "getting PublicShares from etcd failed")
	}

	if r.Count == 0 {
		return nil, SharesNotFound
	}
	if r.Count != 1 {
		return nil, WrongNumberOfResults
	}

	pubShares := &pb.PublicShares{}
	err = proto.Unmarshal(r.Kvs[0].Value, pubShares)
	return pubShares, errors.Wrap(err, "unmarshaling PublicShares failed")
}

func (e *EtcdStore) GetPrivateShares(ctx context.Context, secretId, receiver string) (*pb.PrivateShares, error) {
	r, err := e.etcdclient.Get(ctx, privateSharesKeyForReceiver(secretId, receiver), clientv3.WithPrefix())
	if err != nil {
		return nil, errors.Wrap(err, "getting PrivateShares from etcd failed")
	}

	if r.Count == 0 {
		return nil, SharesNotFound
	}
	privShares := make([]*pb.PrivateShare, r.Count)
	for i, kv := range r.Kvs {
		privShare := &pb.PrivateShare{}
		err = proto.Unmarshal(kv.Value, privShare)
		if err != nil {
			return nil, errors.Wrap(err, "unmarshaling PrivateShare failed")
		}
		privShares[i] = privShare
	}

	return &pb.PrivateShares{
		Items: privShares,
	}, nil
}

func (e *EtcdStore) GetCipherText(ctx context.Context, secretId string) (*pb.CipherText, error) {
	r, err := e.etcdclient.Get(ctx, cipherTextKey(secretId))
	if err != nil {
		return nil, errors.Wrap(err, "getting CipherText from etcd failed")
	}

	if r.Count == 0 {
		return nil, CipherTextNotFound
	}
	if r.Count != 1 {
		return nil, WrongNumberOfResults
	}

	ct := &pb.CipherText{}
	err = proto.Unmarshal(r.Kvs[0].Value, ct)
	if err != nil {
		return nil, errors.Wrap(err, "unmarshaling CipherText failed")
	}

	return ct, nil
}

func secretKey(secretId string) string {
	return "/secrets/" + secretId + "/secret"
}

func sharesKey(secretId string) string {
	return "/secrets/" + secretId + "/shares"
}

func publicSharesKey(secretId string) string {
	return sharesKey(secretId) + "/public"
}

func privateSharesKey(secretId string) string {
	return sharesKey(secretId) + "/private"
}

func privateSharesKeyForReceiver(secretId, receiver string) string {
	return privateSharesKey(secretId) + "/" + receiver
}

func privateShareKey(secretId, receiver, uuid string) string {
	return privateSharesKeyForReceiver(secretId, receiver) + "/" + uuid
}

func cipherTextKey(secretId string) string {
	return "/secrets/" + secretId + "/cipher_text"
}
<commit_msg>no error on no found private shares<commit_after>// Copyright 2017 The hlin 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 store

import (
	pb "github.com/brancz/hlin/pkg/api/apipb"

	"github.com/coreos/etcd/clientv3"
	"github.com/go-kit/kit/log"
	proto "github.com/gogo/protobuf/proto"
	"github.com/pkg/errors"
	uuid "github.com/satori/go.uuid"
	"golang.org/x/net/context"
)

var SecretNotFound = errors.New("secret not found")
var SharesNotFound = errors.New("shares not found")
var CipherTextNotFound = errors.New("cipher text not found")

type SecretStore interface {
	CreateSecret(ctx context.Context, secretId string, s *pb.CreateSecretRequest) (*pb.PlainSecret, error)
	GetSecret(ctx context.Context, secretId string) (*pb.PlainSecret, error)
	GetPublicShares(ctx context.Context, secretId string) (*pb.PublicShares, error)
	GetPrivateShares(ctx context.Context, secretId, receiver string) (*pb.PrivateShares, error)
	GetCipherText(ctx context.Context, secretId string) (*pb.CipherText, error)
}

var WrongNumberOfResults = errors.New("incorrect number of results from etcd")

type EtcdStore struct {
	etcdclient *clientv3.Client
	logger     log.Logger
}

func NewEtcdStore(c *clientv3.Client, logger log.Logger) SecretStore {
	return &EtcdStore{
		etcdclient: c,
		logger:     logger,
	}
}

func (e *EtcdStore) CreateSecret(ctx context.Context, secretId string, s *pb.CreateSecretRequest) (*pb.PlainSecret, error) {
	ps := &pb.PlainSecret{SecretId: secretId}
	out, err := proto.Marshal(ps)
	if err != nil {
		return nil, errors.Wrap(err, "failed to encode PlainSecret")
	}

	_, err = e.etcdclient.Put(ctx, secretKey(secretId), string(out))
	if err != nil {
		return nil, errors.Wrap(err, "Secret could not be saved to etcd")
	}

	out, err = proto.Marshal(s.Shares.Public)
	if err != nil {
		return nil, errors.Wrap(err, "failed to encode PublicShare")
	}
	_, err = e.etcdclient.Put(ctx, publicSharesKey(secretId), string(out))
	if err != nil {
		return nil, errors.Wrap(err, "PublicShare could not be saved to etcd")
	}

	for _, share := range s.Shares.Private.Items {
		out, err = proto.Marshal(share)
		if err != nil {
			return nil, errors.Wrap(err, "failed to encode PublicShare")
		}
		_, err = e.etcdclient.Put(ctx, privateShareKey(secretId, share.Receiver, uuid.NewV4().String()), string(out))
		if err != nil {
			return nil, errors.Wrap(err, "PublicShare could not be saved to etcd")
		}
	}

	out, err = proto.Marshal(s.CipherText)
	if err != nil {
		return nil, errors.Wrap(err, "failed to encode CipherText")
	}
	_, err = e.etcdclient.Put(ctx, cipherTextKey(secretId), string(out))
	if err != nil {
		return nil, errors.Wrap(err, "CipherText could not be saved to etcd")
	}

	return ps, nil
}

func (e *EtcdStore) GetSecret(ctx context.Context, secretId string) (*pb.PlainSecret, error) {
	r, err := e.etcdclient.Get(ctx, secretKey(secretId))
	if err != nil {
		return nil, errors.Wrap(err, "getting secret from etcd failed")
	}

	if r.Count == 0 {
		return nil, SecretNotFound
	}
	if r.Count != 1 {
		return nil, WrongNumberOfResults
	}

	ps := &pb.PlainSecret{}
	err = proto.Unmarshal(r.Kvs[0].Value, ps)
	if err != nil {
		return nil, errors.Wrap(err, "unmarshaling PlainSecret failed")
	}

	return ps, nil
}

func (e *EtcdStore) GetPublicShares(ctx context.Context, secretId string) (*pb.PublicShares, error) {
	r, err := e.etcdclient.Get(ctx, publicSharesKey(secretId))
	if err != nil {
		return nil, errors.Wrap(err, "getting PublicShares from etcd failed")
	}

	if r.Count == 0 {
		return nil, SharesNotFound
	}
	if r.Count != 1 {
		return nil, WrongNumberOfResults
	}

	pubShares := &pb.PublicShares{}
	err = proto.Unmarshal(r.Kvs[0].Value, pubShares)
	return pubShares, errors.Wrap(err, "unmarshaling PublicShares failed")
}

func (e *EtcdStore) GetPrivateShares(ctx context.Context, secretId, receiver string) (*pb.PrivateShares, error) {
	r, err := e.etcdclient.Get(ctx, privateSharesKeyForReceiver(secretId, receiver), clientv3.WithPrefix())
	if err != nil {
		return nil, errors.Wrap(err, "getting PrivateShares from etcd failed")
	}

	privShares := make([]*pb.PrivateShare, r.Count)
	for i, kv := range r.Kvs {
		privShare := &pb.PrivateShare{}
		err = proto.Unmarshal(kv.Value, privShare)
		if err != nil {
			return nil, errors.Wrap(err, "unmarshaling PrivateShare failed")
		}
		privShares[i] = privShare
	}

	return &pb.PrivateShares{
		Items: privShares,
	}, nil
}

func (e *EtcdStore) GetCipherText(ctx context.Context, secretId string) (*pb.CipherText, error) {
	r, err := e.etcdclient.Get(ctx, cipherTextKey(secretId))
	if err != nil {
		return nil, errors.Wrap(err, "getting CipherText from etcd failed")
	}

	if r.Count == 0 {
		return nil, CipherTextNotFound
	}
	if r.Count != 1 {
		return nil, WrongNumberOfResults
	}

	ct := &pb.CipherText{}
	err = proto.Unmarshal(r.Kvs[0].Value, ct)
	if err != nil {
		return nil, errors.Wrap(err, "unmarshaling CipherText failed")
	}

	return ct, nil
}

func secretKey(secretId string) string {
	return "/secrets/" + secretId + "/secret"
}

func sharesKey(secretId string) string {
	return "/secrets/" + secretId + "/shares"
}

func publicSharesKey(secretId string) string {
	return sharesKey(secretId) + "/public"
}

func privateSharesKey(secretId string) string {
	return sharesKey(secretId) + "/private"
}

func privateSharesKeyForReceiver(secretId, receiver string) string {
	return privateSharesKey(secretId) + "/" + receiver
}

func privateShareKey(secretId, receiver, uuid string) string {
	return privateSharesKeyForReceiver(secretId, receiver) + "/" + uuid
}

func cipherTextKey(secretId string) string {
	return "/secrets/" + secretId + "/cipher_text"
}
<|endoftext|>


<commit_before>package util

import (
	"github.com/spf13/cobra"
	core "k8s.io/api/core/v1"
	cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
	"k8s.io/kubernetes/pkg/kubectl/resource"
)

func AddGetFlags(cmd *cobra.Command) {
	cmd.Flags().StringP("namespace", "n", core.NamespaceDefault, "List the requested object(s) from this namespace.")
	cmd.Flags().StringP("selector", "l", "", "Selector (label query) to filter on, supports '=', '==', and '!='.")
	cmd.Flags().Bool("all-namespaces", false, "If present, list the requested object(s) across all namespaces. Namespace specified with --namespace will be ignored.")
	cmd.Flags().Bool("show-kind", false, "If present, list the resource type for the requested object(s).")
	cmd.Flags().StringP("output", "o", "", "Output format. One of: json|yaml|wide|name.")
	cmd.Flags().BoolP("show-all", "a", false, "When printing, show all resources (default hide terminated pods.)")
	cmd.Flags().Bool("show-labels", false, "When printing, show all labels as the last column (default hide labels column)")
}

func AddCreateFlags(cmd *cobra.Command, options *resource.FilenameOptions) {
	cmd.Flags().StringP("namespace", "n", core.NamespaceDefault, "Create object(s) in this namespace.")
	usage := "create the resource"
	AddFilenameOptionFlags(cmd, options, usage)
}

func AddDeleteFlags(cmd *cobra.Command, options *resource.FilenameOptions) {
	cmd.Flags().StringP("namespace", "n", core.NamespaceDefault, "Delete object(s) from this namespace.")
	cmd.Flags().BoolP("all", "", false, "Delete all resources, including uninitialized ones, in the namespace of the specified resource types.")
	cmd.Flags().BoolP("force", "", false, "Immediate deletion of some resources may result in inconsistency or data loss.")
	cmd.Flags().StringP("selector", "l", "", "Selector (label query) to filter on.")
	cmd.Flags().StringP("output", "o", "", "Output mode. Use \"-o name\" for shorter output (resource/name).")
	usage := "delete the resource"
	AddFilenameOptionFlags(cmd, options, usage)
}

func AddDescribeFlags(cmd *cobra.Command) {
	cmd.Flags().StringP("namespace", "n", core.NamespaceDefault, "Describe object(s) from this namespace.")
	cmd.Flags().StringP("selector", "l", "", "Selector (label query) to filter on, supports '=', '==', and '!='.")
	cmd.Flags().Bool("all-namespaces", false, "If present, describe the requested object(s) across all namespaces. Namespace specified with --namespace will be ignored.")
}

func AddEditFlags(cmd *cobra.Command) {
	cmd.Flags().StringP("namespace", "n", core.NamespaceDefault, "Edit object(s) in this namespace.")
	cmd.Flags().StringP("output", "o", "yaml", "Output format. One of: yaml|json.")
}

func AddInitFlags(cmd *cobra.Command) {
	cmd.Flags().String("operator-namespace", "kube-system", "Name of namespace where operator will be deployed.")
	cmd.Flags().String("version", "0.8.0-beta.0", "Operator version")
	cmd.Flags().Bool("upgrade", false, "If present, Upgrade operator to use provided version")
	cmd.Flags().String("governing-service", "kubedb", "Governing service for database statefulset")
	cmd.Flags().String("docker-registry", "kubedb", "User provided docker repository")
	cmd.Flags().String("image-pull-secret", "", " Reference to secret in the same namespace")
	cmd.Flags().String("address", ":8080", "Address to listen on for web interface and telemetry.")
	cmd.Flags().Bool("rbac", false, "If true, uses RBAC with operator and database objects")
}

func AddFilenameOptionFlags(cmd *cobra.Command, options *resource.FilenameOptions, usage string) {
	cmd.Flags().StringSliceVarP(&options.Filenames, "filename", "f", options.Filenames, "Filename to use to "+usage)
	cmd.Flags().BoolVarP(&options.Recursive, "recursive", "R", options.Recursive, "Process the directory used in -f, --filename recursively.")
}

func GetNamespace(cmd *cobra.Command) (string, bool) {
	return cmdutil.GetFlagString(cmd, "namespace"), cmd.Flags().Changed("namespace")
}

func AddAuditReportFlags(cmd *cobra.Command) {
	cmd.Flags().StringP("namespace", "n", core.NamespaceDefault, "Export summary report of the requested object from this namespace.")
	cmd.Flags().String("operator-namespace", "kube-system", "Name of namespace where operator is running")
	cmd.Flags().String("index", "", "Export summary report for this only.")
	cmd.Flags().String("output", "", "Directory used to store summary report")
}

func AddCompareFlags(cmd *cobra.Command) {
	cmd.Flags().String("output", "", "Directory used to store summary report")
	cmd.Flags().Bool("show", true, "If true, comparison result will be printed.")
}
<commit_msg>Remove InitFlags (#178)<commit_after>package util

import (
	"github.com/spf13/cobra"
	core "k8s.io/api/core/v1"
	cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
	"k8s.io/kubernetes/pkg/kubectl/resource"
)

func AddGetFlags(cmd *cobra.Command) {
	cmd.Flags().StringP("namespace", "n", core.NamespaceDefault, "List the requested object(s) from this namespace.")
	cmd.Flags().StringP("selector", "l", "", "Selector (label query) to filter on, supports '=', '==', and '!='.")
	cmd.Flags().Bool("all-namespaces", false, "If present, list the requested object(s) across all namespaces. Namespace specified with --namespace will be ignored.")
	cmd.Flags().Bool("show-kind", false, "If present, list the resource type for the requested object(s).")
	cmd.Flags().StringP("output", "o", "", "Output format. One of: json|yaml|wide|name.")
	cmd.Flags().BoolP("show-all", "a", false, "When printing, show all resources (default hide terminated pods.)")
	cmd.Flags().Bool("show-labels", false, "When printing, show all labels as the last column (default hide labels column)")
}

func AddCreateFlags(cmd *cobra.Command, options *resource.FilenameOptions) {
	cmd.Flags().StringP("namespace", "n", core.NamespaceDefault, "Create object(s) in this namespace.")
	usage := "create the resource"
	AddFilenameOptionFlags(cmd, options, usage)
}

func AddDeleteFlags(cmd *cobra.Command, options *resource.FilenameOptions) {
	cmd.Flags().StringP("namespace", "n", core.NamespaceDefault, "Delete object(s) from this namespace.")
	cmd.Flags().BoolP("all", "", false, "Delete all resources, including uninitialized ones, in the namespace of the specified resource types.")
	cmd.Flags().BoolP("force", "", false, "Immediate deletion of some resources may result in inconsistency or data loss.")
	cmd.Flags().StringP("selector", "l", "", "Selector (label query) to filter on.")
	cmd.Flags().StringP("output", "o", "", "Output mode. Use \"-o name\" for shorter output (resource/name).")
	usage := "delete the resource"
	AddFilenameOptionFlags(cmd, options, usage)
}

func AddDescribeFlags(cmd *cobra.Command) {
	cmd.Flags().StringP("namespace", "n", core.NamespaceDefault, "Describe object(s) from this namespace.")
	cmd.Flags().StringP("selector", "l", "", "Selector (label query) to filter on, supports '=', '==', and '!='.")
	cmd.Flags().Bool("all-namespaces", false, "If present, describe the requested object(s) across all namespaces. Namespace specified with --namespace will be ignored.")
}

func AddEditFlags(cmd *cobra.Command) {
	cmd.Flags().StringP("namespace", "n", core.NamespaceDefault, "Edit object(s) in this namespace.")
	cmd.Flags().StringP("output", "o", "yaml", "Output format. One of: yaml|json.")
}

func AddFilenameOptionFlags(cmd *cobra.Command, options *resource.FilenameOptions, usage string) {
	cmd.Flags().StringSliceVarP(&options.Filenames, "filename", "f", options.Filenames, "Filename to use to "+usage)
	cmd.Flags().BoolVarP(&options.Recursive, "recursive", "R", options.Recursive, "Process the directory used in -f, --filename recursively.")
}

func GetNamespace(cmd *cobra.Command) (string, bool) {
	return cmdutil.GetFlagString(cmd, "namespace"), cmd.Flags().Changed("namespace")
}

func AddAuditReportFlags(cmd *cobra.Command) {
	cmd.Flags().StringP("namespace", "n", core.NamespaceDefault, "Export summary report of the requested object from this namespace.")
	cmd.Flags().String("operator-namespace", "kube-system", "Name of namespace where operator is running")
	cmd.Flags().String("index", "", "Export summary report for this only.")
	cmd.Flags().String("output", "", "Directory used to store summary report")
}

func AddCompareFlags(cmd *cobra.Command) {
	cmd.Flags().String("output", "", "Directory used to store summary report")
	cmd.Flags().Bool("show", true, "If true, comparison result will be printed.")
}
<|endoftext|>


<commit_before>// Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.

package oci

import (
	"fmt"
	"testing"

	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"

	"github.com/terraform-providers/terraform-provider-oci/httpreplay"
)

var (
	replicationSourceDataSourceRepresentation = map[string]interface{}{
		"bucket":    Representation{repType: Required, create: `${oci_objectstorage_bucket.test_bucket_replication.name}`},
		"namespace": Representation{repType: Required, create: `${oci_objectstorage_bucket.test_bucket.namespace}`},
	}

	ReplicationSourceResourceConfig = ReplicationPolicyResourceDependencies +
		generateResourceFromRepresentationMap("oci_objectstorage_replication_policy", "test_replication_policy", Required, Create, replicationPolicyRepresentation)
)

func TestObjectStorageReplicationSourceResource_basic(t *testing.T) {
	httpreplay.SetScenario("TestObjectStorageReplicationSourceResource_basic")
	defer httpreplay.SaveScenario()

	provider := testAccProvider
	config := testProviderConfig()

	compartmentId := getEnvSettingWithBlankDefault("compartment_ocid")
	compartmentIdVariableStr := fmt.Sprintf("variable \"compartment_id\" { default = \"%s\" }\n", compartmentId)

	datasourceName := "data.oci_objectstorage_replication_sources.test_replication_sources"

	resource.Test(t, resource.TestCase{
		PreCheck: func() { testAccPreCheck(t) },
		Providers: map[string]terraform.ResourceProvider{
			"oci": provider,
		},
		Steps: []resource.TestStep{
			// create replication policy first
			{
				Config: config + compartmentIdVariableStr + ReplicationSourceResourceConfig,
			},
			// verify datasource
			{
				Config: config +
					generateDataSourceFromRepresentationMap("oci_objectstorage_replication_sources", "test_replication_sources", Required, Create, replicationSourceDataSourceRepresentation) +
					compartmentIdVariableStr + ReplicationSourceResourceConfig,
				Check: resource.ComposeAggregateTestCheckFunc(
					resource.TestCheckResourceAttr(datasourceName, "bucket", replicationBucketName),
					resource.TestCheckResourceAttr(datasourceName, "namespace", "dxterraformdev"),
					resource.TestCheckResourceAttrSet(datasourceName, "replication_sources.#"),
					resource.TestCheckResourceAttrSet(datasourceName, "replication_sources.0.policy_name"),
					resource.TestCheckResourceAttrSet(datasourceName, "replication_sources.0.source_bucket_name"),
					resource.TestCheckResourceAttrSet(datasourceName, "replication_sources.0.source_region_name"),
				),
			},
		},
	})
}
<commit_msg>change checkresourceattr to checkresourceattrset to avoid error for teamcity test<commit_after>// Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.

package oci

import (
	"fmt"
	"testing"

	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"

	"github.com/terraform-providers/terraform-provider-oci/httpreplay"
)

var (
	replicationSourceDataSourceRepresentation = map[string]interface{}{
		"bucket":    Representation{repType: Required, create: `${oci_objectstorage_bucket.test_bucket_replication.name}`},
		"namespace": Representation{repType: Required, create: `${oci_objectstorage_bucket.test_bucket.namespace}`},
	}

	ReplicationSourceResourceConfig = ReplicationPolicyResourceDependencies +
		generateResourceFromRepresentationMap("oci_objectstorage_replication_policy", "test_replication_policy", Required, Create, replicationPolicyRepresentation)
)

func TestObjectStorageReplicationSourceResource_basic(t *testing.T) {
	httpreplay.SetScenario("TestObjectStorageReplicationSourceResource_basic")
	defer httpreplay.SaveScenario()

	provider := testAccProvider
	config := testProviderConfig()

	compartmentId := getEnvSettingWithBlankDefault("compartment_ocid")
	compartmentIdVariableStr := fmt.Sprintf("variable \"compartment_id\" { default = \"%s\" }\n", compartmentId)

	datasourceName := "data.oci_objectstorage_replication_sources.test_replication_sources"

	resource.Test(t, resource.TestCase{
		PreCheck: func() { testAccPreCheck(t) },
		Providers: map[string]terraform.ResourceProvider{
			"oci": provider,
		},
		Steps: []resource.TestStep{
			// create replication policy first
			{
				Config: config + compartmentIdVariableStr + ReplicationSourceResourceConfig,
			},
			// verify datasource
			{
				Config: config +
					generateDataSourceFromRepresentationMap("oci_objectstorage_replication_sources", "test_replication_sources", Required, Create, replicationSourceDataSourceRepresentation) +
					compartmentIdVariableStr + ReplicationSourceResourceConfig,
				Check: resource.ComposeAggregateTestCheckFunc(
					resource.TestCheckResourceAttr(datasourceName, "bucket", replicationBucketName),
					resource.TestCheckResourceAttrSet(datasourceName, "namespace"),
					resource.TestCheckResourceAttrSet(datasourceName, "replication_sources.#"),
					resource.TestCheckResourceAttrSet(datasourceName, "replication_sources.0.policy_name"),
					resource.TestCheckResourceAttrSet(datasourceName, "replication_sources.0.source_bucket_name"),
					resource.TestCheckResourceAttrSet(datasourceName, "replication_sources.0.source_region_name"),
				),
			},
		},
	})
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"bytes"
	"crypto/rand"
	storage "github.com/MSOpenTech/azure-sdk-for-go/storage"
)

func getBlobClient() (*storage.BlobStorageClient, error) {
	name := os.Getenv("STORAGE_ACCOUNT_NAME")
	key := os.Getenv("STORAGE_KEY")
	cli, err := storage.NewBasicClient(name, key)

	if err != nil {
		return nil, err
	}

	return cli.GetBlobService(), nil
}

func main() {
    cli, err := getBlobClient()

   	if err != nil {
		fmt.Printf("Error trying to check if container exists!\n")
		return
	}

	cnt := "testcontainer"
	ok, err := cli.ContainerExists(cnt)
	if err != nil {
		fmt.Printf("Error trying to check if container exists!\n")
	}

	if ok {
		fmt.Printf("Found container!\n")
	} else {
		fmt.Printf("Containr not found");
	}

	blob_operations(cli);
}

func blob_operations(cli *storage.BlobStorageClient) {
	cnt := "testcontainer"
	blob := "testblob-" + randString(10);
	body := []byte(randString(1024))

	// Create the blob and add contents to it
	err := cli.PutBlockBlob(cnt, blob, bytes.NewReader(body))

	// Validate that the blob exists
	ok, err := cli.BlobExists(cnt, blob)
	if err != nil {
		fmt.Printf("Error trying to check if container exists!\n")
	}

	if ok {
		fmt.Printf("Found blob!\n")
	} else {
		fmt.Printf("Blob not found");
	}
}

func randString(n int) string {
	if n <= 0 {
		panic("negative number")
	}
	const alphanum = "0123456789abcdefghijklmnopqrstuvwxyz"
	var bytes = make([]byte, n)
	rand.Read(bytes)
	for i, b := range bytes {
		bytes[i] = alphanum[b%byte(len(alphanum))]
	}
	return string(bytes)
}
<commit_msg>retrieve blob contents and validate<commit_after>package main

import (
	"fmt"
	"os"
	"bytes"
	"crypto/rand"
	"io/ioutil"
	"reflect"
	storage "github.com/MSOpenTech/azure-sdk-for-go/storage"
)

func getBlobClient() (*storage.BlobStorageClient, error) {
	name := os.Getenv("STORAGE_ACCOUNT_NAME")
	key := os.Getenv("STORAGE_KEY")
	cli, err := storage.NewBasicClient(name, key)

	if err != nil {
		return nil, err
	}

	return cli.GetBlobService(), nil
}

func main() {
    cli, err := getBlobClient()

   	if err != nil {
		fmt.Printf("Error trying to check if container exists!\n")
		return
	}

	cnt := "testcontainer"
	ok, err := cli.ContainerExists(cnt)
	if err != nil {
		fmt.Printf("Error trying to check if container exists!\n")
	}

	if ok {
		fmt.Printf("Found container!\n")
	} else {
		fmt.Printf("Containr not found");
	}

	blob_operations(cli);
}

func blob_operations(cli *storage.BlobStorageClient) {
	cnt := "testcontainer"
	blob := "testblob-" + randString(10);
	body := []byte(randString(64))

	fmt.Print("Creating blob ", blob,  " and putting contents into it ", body)

	// Create the blob and add contents to it
	err := cli.PutBlockBlob(cnt, blob, bytes.NewReader(body))

	// Validate that the blob exists
	ok, err := cli.BlobExists(cnt, blob)
	if err != nil {
		fmt.Printf("Error trying to check if container exists!\n")
	}

	if ok {
		fmt.Printf("Found blob!\n")
	} else {
		fmt.Printf("Blob not found");
	}

	// Validate that the contents of the blob are as expected

	resp, err := cli.GetBlob(cnt, blob)
	if err != nil {
		fmt.Printf("Error trying to check blob contents!\n")
	}

	// Verify contents
	respBody, err := ioutil.ReadAll(resp)
	defer resp.Close()
	if err != nil {
		fmt.Printf("Error trying to get blob contents!\n")
	}

	if !reflect.DeepEqual(body, respBody) {
		fmt.Printf("Wrong blob contents.\nExpected: %d bytes, Got: %d byes", len(body), len(respBody))
	}

	fmt.Print("Validated that blob ", blob, " has contents: ", resp)

}

func randString(n int) string {
	if n <= 0 {
		panic("negative number")
	}
	const alphanum = "0123456789abcdefghijklmnopqrstuvwxyz"
	var bytes = make([]byte, n)
	rand.Read(bytes)
	for i, b := range bytes {
		bytes[i] = alphanum[b%byte(len(alphanum))]
	}
	return string(bytes)
}
<|endoftext|>


<commit_before>package azure

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"reflect"
	"strconv"
	"strings"
	"time"

	"github.com/hashicorp/go-retryablehttp"
	"github.com/mitchellh/mapstructure"
)

type Request struct {
	URI      *string
	location *string
	tags     *map[string]*string
	etag     *string
	Command  APICall

	client *Client
}

func readTaggedFields(command interface{}) map[string]interface{} {
	var value reflect.Value
	if reflect.ValueOf(command).Kind() == reflect.Ptr {
		value = reflect.ValueOf(command).Elem()
	} else {
		value = reflect.ValueOf(command)
	}

	result := make(map[string]interface{})

	for i := 0; i < value.NumField(); i++ { // iterates through every struct type field
		fmt.Println(i, value.Type().Field(i).Name)
		tag := value.Type().Field(i).Tag // returns the tag string
		tagValue := tag.Get("riviera")
		if tagValue != "" {
			result[tagValue] = value.Field(i).Interface()
		}
	}
	return result
}

func (request *Request) pollForAsynchronousResponse(acceptedResponse *http.Response) (*http.Response, error) {
	var resp *http.Response = acceptedResponse

	for {
		if resp.StatusCode != http.StatusAccepted {
			return resp, nil
		}

		if retryAfter := resp.Header.Get("Retry-After"); retryAfter != "" {
			retryTime, err := strconv.Atoi(strings.TrimSpace(retryAfter))
			if err != nil {
				return nil, err
			}

			request.client.logger.Printf("[INFO] Polling pausing for %d seconds as per Retry-After header", retryTime)
			time.Sleep(time.Duration(retryTime) * time.Second)
		}

		pollLocation, err := resp.Location()
		if err != nil {
			return nil, err
		}

		request.client.logger.Printf("[INFO] Polling %q for operation completion", pollLocation.String())
		req, err := retryablehttp.NewRequest("GET", pollLocation.String(), bytes.NewReader([]byte{}))
		if err != nil {
			return nil, err
		}

		err = request.client.tokenRequester.addAuthorizationToRequest(req)
		if err != nil {
			return nil, err
		}

		resp, err := request.client.httpClient.Do(req)
		if err != nil {
			return nil, err
		}
		if resp.StatusCode == http.StatusAccepted {
			continue
		}

		return resp, err
	}
}

func defaultARMRequestStruct(request *Request, properties interface{}) interface{} {
	body := make(map[string]interface{})

	envelopeFields := readTaggedFields(properties)
	for k, v := range envelopeFields {
		body[k] = v
	}

	body["properties"] = properties
	return body
}

func defaultARMRequestSerialize(body interface{}) (io.ReadSeeker, error) {
	jsonEncodedRequest, err := json.Marshal(body)
	if err != nil {
		return nil, err
	}
	return bytes.NewReader(jsonEncodedRequest), nil
}

func (request *Request) Execute() (*Response, error) {
	apiInfo := request.Command.APIInfo()

	var urlString string

	// Base URL should already be validated by now so Parse is safe without error handling
	urlObj, _ := url.Parse(request.client.BaseURL)

	// Determine whether to use the URLPathFunc or the URI explicitly set in the request
	if request.URI == nil {
		urlObj.Path = fmt.Sprintf("/subscriptions/%s/%s", request.client.subscriptionID, strings.TrimPrefix(apiInfo.URLPathFunc(), "/"))
		urlString = urlObj.String()
	} else {
		urlObj.Path = *request.URI
		urlString = urlObj.String()
	}

	// Encode the request body if necessary
	var body io.ReadSeeker
	if apiInfo.HasBody() {
		var bodyStruct interface{}
		if apiInfo.RequestPropertiesFunc != nil {
			bodyStruct = defaultARMRequestStruct(request, apiInfo.RequestPropertiesFunc())
		} else {
			bodyStruct = defaultARMRequestStruct(request, request.Command)
		}

		serialized, err := defaultARMRequestSerialize(bodyStruct)
		if err != nil {
			return nil, err
		}

		body = serialized
	} else {

		body = bytes.NewReader([]byte{})
	}

	// Create an HTTP request
	req, err := retryablehttp.NewRequest(apiInfo.Method, urlString, body)
	if err != nil {
		return nil, err
	}

	query := req.URL.Query()
	query.Set("api-version", apiInfo.APIVersion)
	req.URL.RawQuery = query.Encode()

	if apiInfo.HasBody() {
		req.Header.Add("Content-Type", "application/json")
	}

	err = request.client.tokenRequester.addAuthorizationToRequest(req)
	if err != nil {
		return nil, err
	}

	httpResponse, err := request.client.httpClient.Do(req)
	if err != nil {
		return nil, err
	}

	// This is safe to use for every request: we check for it being http.StatusAccepted
	httpResponse, err = request.pollForAsynchronousResponse(httpResponse)
	if err != nil {
		return nil, err
	}

	var responseObj interface{}
	var errorObj *Error

	if isSuccessCode(httpResponse.StatusCode) {
		responseObj = apiInfo.ResponseTypeFunc()
		// The response factory func returns nil as a signal that there is no body
		if responseObj != nil {
			responseMap, err := unmarshalFlattenPropertiesAndClose(httpResponse)
			if err != nil {
				return nil, err
			}

			err = mapstructure.WeakDecode(responseMap, responseObj)
			if err != nil {
				return nil, err
			}
		}
	} else {
		responseMap, err := unmarshalFlattenErrorAndClose(httpResponse)

		err = mapstructure.WeakDecode(responseMap, &errorObj)
		if err != nil {
			return nil, err
		}

		errorObj.StatusCode = httpResponse.StatusCode
	}

	return &Response{
		HTTP:   httpResponse,
		Parsed: responseObj,
		Error:  errorObj,
	}, nil
}
<commit_msg>Remove unncessary logging<commit_after>package azure

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"reflect"
	"strconv"
	"strings"
	"time"

	"github.com/hashicorp/go-retryablehttp"
	"github.com/mitchellh/mapstructure"
)

type Request struct {
	URI      *string
	location *string
	tags     *map[string]*string
	etag     *string
	Command  APICall

	client *Client
}

func readTaggedFields(command interface{}) map[string]interface{} {
	var value reflect.Value
	if reflect.ValueOf(command).Kind() == reflect.Ptr {
		value = reflect.ValueOf(command).Elem()
	} else {
		value = reflect.ValueOf(command)
	}

	result := make(map[string]interface{})

	for i := 0; i < value.NumField(); i++ { // iterates through every struct type field
		tag := value.Type().Field(i).Tag // returns the tag string
		tagValue := tag.Get("riviera")
		if tagValue != "" {
			result[tagValue] = value.Field(i).Interface()
		}
	}
	return result
}

func (request *Request) pollForAsynchronousResponse(acceptedResponse *http.Response) (*http.Response, error) {
	var resp *http.Response = acceptedResponse

	for {
		if resp.StatusCode != http.StatusAccepted {
			return resp, nil
		}

		if retryAfter := resp.Header.Get("Retry-After"); retryAfter != "" {
			retryTime, err := strconv.Atoi(strings.TrimSpace(retryAfter))
			if err != nil {
				return nil, err
			}

			request.client.logger.Printf("[INFO] Polling pausing for %d seconds as per Retry-After header", retryTime)
			time.Sleep(time.Duration(retryTime) * time.Second)
		}

		pollLocation, err := resp.Location()
		if err != nil {
			return nil, err
		}

		request.client.logger.Printf("[INFO] Polling %q for operation completion", pollLocation.String())
		req, err := retryablehttp.NewRequest("GET", pollLocation.String(), bytes.NewReader([]byte{}))
		if err != nil {
			return nil, err
		}

		err = request.client.tokenRequester.addAuthorizationToRequest(req)
		if err != nil {
			return nil, err
		}

		resp, err := request.client.httpClient.Do(req)
		if err != nil {
			return nil, err
		}
		if resp.StatusCode == http.StatusAccepted {
			continue
		}

		return resp, err
	}
}

func defaultARMRequestStruct(request *Request, properties interface{}) interface{} {
	body := make(map[string]interface{})

	envelopeFields := readTaggedFields(properties)
	for k, v := range envelopeFields {
		body[k] = v
	}

	body["properties"] = properties
	return body
}

func defaultARMRequestSerialize(body interface{}) (io.ReadSeeker, error) {
	jsonEncodedRequest, err := json.Marshal(body)
	if err != nil {
		return nil, err
	}
	return bytes.NewReader(jsonEncodedRequest), nil
}

func (request *Request) Execute() (*Response, error) {
	apiInfo := request.Command.APIInfo()

	var urlString string

	// Base URL should already be validated by now so Parse is safe without error handling
	urlObj, _ := url.Parse(request.client.BaseURL)

	// Determine whether to use the URLPathFunc or the URI explicitly set in the request
	if request.URI == nil {
		urlObj.Path = fmt.Sprintf("/subscriptions/%s/%s", request.client.subscriptionID, strings.TrimPrefix(apiInfo.URLPathFunc(), "/"))
		urlString = urlObj.String()
	} else {
		urlObj.Path = *request.URI
		urlString = urlObj.String()
	}

	// Encode the request body if necessary
	var body io.ReadSeeker
	if apiInfo.HasBody() {
		var bodyStruct interface{}
		if apiInfo.RequestPropertiesFunc != nil {
			bodyStruct = defaultARMRequestStruct(request, apiInfo.RequestPropertiesFunc())
		} else {
			bodyStruct = defaultARMRequestStruct(request, request.Command)
		}

		serialized, err := defaultARMRequestSerialize(bodyStruct)
		if err != nil {
			return nil, err
		}

		body = serialized
	} else {

		body = bytes.NewReader([]byte{})
	}

	// Create an HTTP request
	req, err := retryablehttp.NewRequest(apiInfo.Method, urlString, body)
	if err != nil {
		return nil, err
	}

	query := req.URL.Query()
	query.Set("api-version", apiInfo.APIVersion)
	req.URL.RawQuery = query.Encode()

	if apiInfo.HasBody() {
		req.Header.Add("Content-Type", "application/json")
	}

	err = request.client.tokenRequester.addAuthorizationToRequest(req)
	if err != nil {
		return nil, err
	}

	httpResponse, err := request.client.httpClient.Do(req)
	if err != nil {
		return nil, err
	}

	// This is safe to use for every request: we check for it being http.StatusAccepted
	httpResponse, err = request.pollForAsynchronousResponse(httpResponse)
	if err != nil {
		return nil, err
	}

	var responseObj interface{}
	var errorObj *Error

	if isSuccessCode(httpResponse.StatusCode) {
		responseObj = apiInfo.ResponseTypeFunc()
		// The response factory func returns nil as a signal that there is no body
		if responseObj != nil {
			responseMap, err := unmarshalFlattenPropertiesAndClose(httpResponse)
			if err != nil {
				return nil, err
			}

			err = mapstructure.WeakDecode(responseMap, responseObj)
			if err != nil {
				return nil, err
			}
		}
	} else {
		responseMap, err := unmarshalFlattenErrorAndClose(httpResponse)

		err = mapstructure.WeakDecode(responseMap, &errorObj)
		if err != nil {
			return nil, err
		}

		errorObj.StatusCode = httpResponse.StatusCode
	}

	return &Response{
		HTTP:   httpResponse,
		Parsed: responseObj,
		Error:  errorObj,
	}, nil
}
<|endoftext|>


<commit_before>package fuse

import (
	"bytes"
	"syscall"
	"fmt"
	"unsafe"
)

var _ = fmt.Print

// TODO - move this into the Go distribution.

func getxattr(path string, attr string, dest []byte) (sz int, errno int) {
	pathBs := []byte(path)
	attrBs := []byte(attr)
	size, _, errNo := syscall.Syscall6(
		syscall.SYS_GETXATTR,
		uintptr(unsafe.Pointer(&pathBs[0])),
		uintptr(unsafe.Pointer(&attrBs[0])),
		uintptr(unsafe.Pointer(&dest[0])),
		uintptr(len(dest)),
		0, 0)
	return int(size), int(errNo)
}

func GetXAttr(path string, attr string) (value []byte, errno int) {
	dest := make([]byte, 1024)
	sz, errno := getxattr(path, attr, dest)

	for sz > cap(dest) && errno == 0 {
		dest = make([]byte, sz)
		sz, errno = getxattr(path, attr, dest)
	}

	if errno != 0 {
		return nil, errno
	}

	return dest[:sz], errno
}

func listxattr(path string, dest []byte) (sz int, errno int) {
	pathbs := []byte(path)
	size, _, errNo := syscall.Syscall(
		syscall.SYS_LISTXATTR,
		uintptr(unsafe.Pointer(&pathbs[0])),
		uintptr(unsafe.Pointer(&dest[0])),
		uintptr(len(dest)))

	return int(size), int(errNo)
}

func ListXAttr(path string) (attributes [][]byte, errno int) {
	dest := make([]byte, 1024)
	sz, errno := listxattr(path, dest)
	if errno != 0 {
		return nil, errno
	}

	for sz > cap(dest) && errno == 0 {
		dest = make([]byte, sz)
		sz, errno = listxattr(path, dest)
	}

	// -1 to drop the final empty slice.
	dest = dest[:sz-1]
	attributes = bytes.Split(dest, []byte{0}, -1)
	return attributes, errno
}
<commit_msg>Properly null-terminate strings before calling Syscall.<commit_after>package fuse

import (
	"bytes"
	"syscall"
	"fmt"
	"unsafe"
)

var _ = fmt.Print

// TODO - move this into the Go distribution.

func getxattr(path string, attr string, dest []byte) (sz int, errno int) {
	pathBs := syscall.StringBytePtr(path)
	attrBs := syscall.StringBytePtr(attr)
	size, _, errNo := syscall.Syscall6(
		syscall.SYS_GETXATTR,
		uintptr(unsafe.Pointer(pathBs)),
		uintptr(unsafe.Pointer(attrBs)),
		uintptr(unsafe.Pointer(&dest[0])),
		uintptr(len(dest)),
		0, 0)
	return int(size), int(errNo)
}

func GetXAttr(path string, attr string) (value []byte, errno int) {
	dest := make([]byte, 1024)
	sz, errno := getxattr(path, attr, dest)

	for sz > cap(dest) && errno == 0 {
		dest = make([]byte, sz)
		sz, errno = getxattr(path, attr, dest)
	}

	if errno != 0 {
		return nil, errno
	}

	return dest[:sz], errno
}

func listxattr(path string, dest []byte) (sz int, errno int) {
	pathbs := syscall.StringBytePtr(path)
	size, _, errNo := syscall.Syscall(
		syscall.SYS_LISTXATTR,
		uintptr(unsafe.Pointer(pathbs)),
		uintptr(unsafe.Pointer(&dest[0])),
		uintptr(len(dest)))

	return int(size), int(errNo)
}

func ListXAttr(path string) (attributes [][]byte, errno int) {
	dest := make([]byte, 1024)
	sz, errno := listxattr(path, dest)
	if errno != 0 {
		return nil, errno
	}

	for sz > cap(dest) && errno == 0 {
		dest = make([]byte, sz)
		sz, errno = listxattr(path, dest)
	}

	// -1 to drop the final empty slice.
	dest = dest[:sz-1]
	attributes = bytes.Split(dest, []byte{0}, -1)
	return attributes, errno
}
<|endoftext|>


<commit_before>package db

import (
	"database/sql"
	"errors"
	"fmt"
	"log"
	"os"
	"time"

	"github.com/cheggaaa/pb/v3"
	"github.com/inconshreveable/log15"
	"github.com/mattn/go-sqlite3"
	"github.com/spf13/viper"
	c "github.com/vulsio/goval-dictionary/config"
	"github.com/vulsio/goval-dictionary/models"
	"golang.org/x/xerrors"
	"gorm.io/driver/mysql"
	"gorm.io/driver/postgres"
	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
	"gorm.io/gorm/logger"
)

// Supported DB dialects.
const (
	dialectSqlite3    = "sqlite3"
	dialectMysql      = "mysql"
	dialectPostgreSQL = "postgres"
)

// RDBDriver is Driver for RDB
type RDBDriver struct {
	name string
	conn *gorm.DB
}

// Name is driver name
func (r *RDBDriver) Name() string {
	return r.name
}

// OpenDB opens Database
func (r *RDBDriver) OpenDB(dbType, dbPath string, debugSQL bool, _ Option) (locked bool, err error) {
	gormConfig := gorm.Config{
		DisableForeignKeyConstraintWhenMigrating: true,
		Logger: logger.New(
			log.New(os.Stderr, "\r\n", log.LstdFlags),
			logger.Config{
				LogLevel: logger.Silent,
			},
		),
	}

	if debugSQL {
		gormConfig.Logger = logger.New(
			log.New(os.Stderr, "\r\n", log.LstdFlags),
			logger.Config{
				SlowThreshold: time.Second,
				LogLevel:      logger.Info,
				Colorful:      true,
			},
		)
	}

	switch r.name {
	case dialectSqlite3:
		r.conn, err = gorm.Open(sqlite.Open(dbPath), &gormConfig)
	case dialectMysql:
		r.conn, err = gorm.Open(mysql.Open(dbPath), &gormConfig)
	case dialectPostgreSQL:
		r.conn, err = gorm.Open(postgres.Open(dbPath), &gormConfig)
	default:
		err = xerrors.Errorf("Not Supported DB dialects. r.name: %s", r.name)
	}

	if err != nil {
		if r.name == dialectSqlite3 {
			switch err.(sqlite3.Error).Code {
			case sqlite3.ErrLocked, sqlite3.ErrBusy:
				return true, xerrors.Errorf("Failed to open DB. dbtype: %s, dbpath: %s, err: %w", dbType, dbPath, err)
			}
		}
		return false, xerrors.Errorf("Failed to open DB. dbtype: %s, dbpath: %s, err: %w", dbType, dbPath, err)
	}

	if r.name == dialectSqlite3 {
		r.conn.Exec("PRAGMA foreign_keys = ON")
	}
	return false, nil
}

// MigrateDB migrates Database
func (r *RDBDriver) MigrateDB() error {
	if err := r.conn.AutoMigrate(
		&models.FetchMeta{},
		&models.Root{},
		&models.Definition{},
		&models.Package{},
		&models.Reference{},
		&models.Advisory{},
		&models.Cve{},
		&models.Bugzilla{},
		&models.Cpe{},
		&models.Debian{},
	); err != nil {
		return xerrors.Errorf("Failed to migrate. err: %w", err)
	}

	return nil
}

// CloseDB close Database
func (r *RDBDriver) CloseDB() (err error) {
	if r.conn == nil {
		return
	}

	var sqlDB *sql.DB
	if sqlDB, err = r.conn.DB(); err != nil {
		return xerrors.Errorf("Failed to get DB Object. err : %w", err)
	}
	if err = sqlDB.Close(); err != nil {
		return xerrors.Errorf("Failed to close DB. Type: %s. err: %w", r.name, err)
	}
	return
}

// GetByPackName select OVAL definition related to OS Family, osVer, packName
func (r *RDBDriver) GetByPackName(family, osVer, packName, arch string) ([]models.Definition, error) {
	family, osVer, err := formatFamilyAndOSVer(family, osVer)
	if err != nil {
		return nil, xerrors.Errorf("Failed to formatFamilyAndOSVer. err: %w", err)
	}

	q := r.conn.
		Joins("JOIN roots ON roots.id = definitions.root_id AND roots.family= ? AND roots.os_version = ?", family, osVer).
		Joins("JOIN packages ON packages.definition_id = definitions.id").
		Preload("Advisory").
		Preload("Advisory.Cves").
		Preload("Advisory.Bugzillas").
		Preload("Advisory.AffectedCPEList").
		Preload("References")

	switch family {
	case c.Debian:
		q = q.Preload("Debian").Where("`packages`.`name` = ?", packName).Preload("AffectedPacks")
	case c.Amazon, c.Oracle:
		if arch == "" {
			q = q.Where("`packages`.`name` = ?", packName).Preload("AffectedPacks")
		} else {
			q = q.Where("`packages`.`name` = ? AND `packages`.`arch` = ?", packName, arch).Preload("AffectedPacks", "arch = ?", arch)
		}
	default:
		q = q.Where("`packages`.`name` = ?", packName).Preload("AffectedPacks")
	}

	// Specify limit number to avoid `too many SQL variable`.
	// https://github.com/future-architect/vuls/issues/886
	defs := []models.Definition{}
	limit, tmpDefs := 998, []models.Definition{}
	for i := 0; true; i++ {
		err := q.
			Limit(limit).Offset(i * limit).
			Find(&tmpDefs).Error
		if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, err
		}
		if len(tmpDefs) == 0 {
			break
		}
		defs = append(defs, tmpDefs...)
	}

	if family == c.RedHat {
		for i := range defs {
			defs[i].AffectedPacks = filterByRedHatMajor(defs[i].AffectedPacks, major(osVer))
		}
	}

	return defs, nil
}

// GetByCveID select OVAL definition related to OS Family, osVer, cveID
func (r *RDBDriver) GetByCveID(family, osVer, cveID, arch string) ([]models.Definition, error) {
	family, osVer, err := formatFamilyAndOSVer(family, osVer)
	if err != nil {
		return nil, xerrors.Errorf("Failed to formatFamilyAndOSVer. err: %w", err)
	}

	q := r.conn.
		Joins("JOIN roots ON roots.id = definitions.root_id AND roots.family= ? AND roots.os_version = ?", family, osVer).
		Joins("JOIN advisories ON advisories.definition_id = definitions.id").
		Joins("JOIN cves ON cves.advisory_id = advisories.id").
		Where("cves.cve_id = ?", cveID).
		Preload("Advisory").
		Preload("Advisory.Cves").
		Preload("Advisory.Bugzillas").
		Preload("Advisory.AffectedCPEList").
		Preload("References")

	switch family {
	case c.Debian:
		q = q.Preload("Debian").Preload("AffectedPacks")
	case c.Amazon, c.Oracle:
		if arch == "" {
			q = q.Preload("AffectedPacks")
		} else {
			q = q.Preload("AffectedPacks", "arch = ?", arch)
		}
	default:
		q = q.Preload("AffectedPacks")
	}

	defs := []models.Definition{}
	if err := q.Find(&defs).Error; err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
		return nil, err
	}

	if family == c.RedHat {
		for i := range defs {
			defs[i].AffectedPacks = filterByRedHatMajor(defs[i].AffectedPacks, major(osVer))
		}
	}

	return defs, nil
}

// InsertOval inserts OVAL
func (r *RDBDriver) InsertOval(root *models.Root) error {
	family, osVer, err := formatFamilyAndOSVer(root.Family, root.OSVersion)
	if err != nil {
		return xerrors.Errorf("Failed to formatFamilyAndOSVer. err: %w", err)
	}
	log15.Info("Refreshing...", "Family", family, "Version", osVer)

	batchSize := viper.GetInt("batch-size")
	if batchSize < 1 {
		return fmt.Errorf("Failed to set batch-size. err: batch-size option is not set properly")
	}

	tx := r.conn.Begin()
	old := models.Root{}
	result := tx.Where(&models.Root{Family: family, OSVersion: osVer}).First(&old)
	if result.Error != nil && !errors.Is(result.Error, gorm.ErrRecordNotFound) {
		tx.Rollback()
		return xerrors.Errorf("Failed to select old defs: %w", result.Error)
	}

	if result.RowsAffected > 0 {
		log15.Info("Deleting old Definitions...")
		// Delete data related to root passed in arg
		defs := []models.Definition{}
		if err := tx.Model(&old).Association("Definitions").Find(&defs); err != nil {
			tx.Rollback()
			return xerrors.Errorf("Failed to select old defs: %w", err)
		}

		bar := pb.StartNew(len(defs))
		for idx := range chunkSlice(len(defs), 998) {
			var advs []models.Advisory
			if err := tx.Model(defs[idx.From:idx.To]).Association("Advisory").Find(&advs); err != nil {
				tx.Rollback()
				return xerrors.Errorf("Failed to delete: %w", err)
			}

			for idx2 := range chunkSlice(len(advs), 998) {
				if err := tx.Select(clause.Associations).Unscoped().Delete(advs[idx2.From:idx2.To]).Error; err != nil {
					tx.Rollback()
					return xerrors.Errorf("Failed to delete: %w", err)
				}
			}

			if err := tx.Select(clause.Associations).Unscoped().Delete(defs[idx.From:idx.To]).Error; err != nil {
				tx.Rollback()
				return xerrors.Errorf("Failed to delete: %w", err)
			}
			bar.Add(idx.To - idx.From)
		}
		if err := tx.Unscoped().Where("id = ?", old.ID).Delete(&models.Root{}).Error; err != nil {
			tx.Rollback()
			return xerrors.Errorf("Failed to delete: %w", err)
		}
		bar.Finish()
	}

	log15.Info("Inserting new Definitions...")
	bar := pb.StartNew(len(root.Definitions))
	if err := tx.Omit("Definitions").Create(&root).Error; err != nil {
		tx.Rollback()
		return xerrors.Errorf("Failed to insert Root. err: %w", err)
	}

	for i := range root.Definitions {
		root.Definitions[i].RootID = root.ID
	}

	for idx := range chunkSlice(len(root.Definitions), batchSize) {
		if err := tx.Create(root.Definitions[idx.From:idx.To]).Error; err != nil {
			tx.Rollback()
			return xerrors.Errorf("Failed to insert Definitions. err: %w", err)
		}
		bar.Add(idx.To - idx.From)
	}
	bar.Finish()

	return tx.Commit().Error
}

// CountDefs counts the number of definitions specified by args
func (r *RDBDriver) CountDefs(family, osVer string) (int, error) {
	family, osVer, err := formatFamilyAndOSVer(family, osVer)
	if err != nil {
		return 0, xerrors.Errorf("Failed to formatFamilyAndOSVer. err: %w", err)
	}

	root := models.Root{}
	if err := r.conn.Where(&models.Root{Family: family, OSVersion: osVer}).Take(&root).Error; err != nil {
		if !errors.Is(err, gorm.ErrRecordNotFound) {
			return 0, err
		}
		return 0, nil
	}

	var count int64
	if err := r.conn.Model(&models.Definition{}).Where(
		"root_id = ?", root.ID).Count(&count).Error; err != nil {
		return 0, err
	}
	return int(count), nil
}

// GetLastModified get last modified time of OVAL in roots
func (r *RDBDriver) GetLastModified(family, osVer string) (time.Time, error) {
	family, osVer, err := formatFamilyAndOSVer(family, osVer)
	if err != nil {
		return time.Time{}, xerrors.Errorf("Failed to formatFamilyAndOSVer. err: %w", err)
	}

	root := models.Root{}
	result := r.conn.Where(&models.Root{Family: family, OSVersion: osVer}).First(&root)
	if result.Error != nil && !errors.Is(result.Error, gorm.ErrRecordNotFound) {
		return time.Time{}, xerrors.Errorf("Failed to get root: %w", result.Error)
	}

	if result.RowsAffected == 0 {
		now := time.Now()
		return now.AddDate(-100, 0, 0), nil
	}
	return root.Timestamp, nil
}

// IsGovalDictModelV1 determines if the DB was created at the time of goval-dictionary Model v1
func (r *RDBDriver) IsGovalDictModelV1() (bool, error) {
	return r.conn.Migrator().HasColumn(&models.FetchMeta{}, "file_name"), nil
}

// GetFetchMeta get FetchMeta from Database
func (r *RDBDriver) GetFetchMeta() (fetchMeta *models.FetchMeta, err error) {
	if err = r.conn.Take(&fetchMeta).Error; err != nil {
		if !errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, err
		}
		return &models.FetchMeta{GovalDictRevision: c.Revision, SchemaVersion: models.LatestSchemaVersion, LastFetchedAt: time.Date(1000, time.January, 1, 0, 0, 0, 0, time.UTC)}, nil
	}

	return fetchMeta, nil
}

// UpsertFetchMeta upsert FetchMeta to Database
func (r *RDBDriver) UpsertFetchMeta(fetchMeta *models.FetchMeta) error {
	fetchMeta.GovalDictRevision = c.Revision
	fetchMeta.SchemaVersion = models.LatestSchemaVersion
	return r.conn.Save(fetchMeta).Error
}
<commit_msg>fix(rdb): change where query (#200)<commit_after>package db

import (
	"database/sql"
	"errors"
	"fmt"
	"log"
	"os"
	"time"

	"github.com/cheggaaa/pb/v3"
	"github.com/inconshreveable/log15"
	"github.com/mattn/go-sqlite3"
	"github.com/spf13/viper"
	c "github.com/vulsio/goval-dictionary/config"
	"github.com/vulsio/goval-dictionary/models"
	"golang.org/x/xerrors"
	"gorm.io/driver/mysql"
	"gorm.io/driver/postgres"
	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
	"gorm.io/gorm/logger"
)

// Supported DB dialects.
const (
	dialectSqlite3    = "sqlite3"
	dialectMysql      = "mysql"
	dialectPostgreSQL = "postgres"
)

// RDBDriver is Driver for RDB
type RDBDriver struct {
	name string
	conn *gorm.DB
}

// Name is driver name
func (r *RDBDriver) Name() string {
	return r.name
}

// OpenDB opens Database
func (r *RDBDriver) OpenDB(dbType, dbPath string, debugSQL bool, _ Option) (locked bool, err error) {
	gormConfig := gorm.Config{
		DisableForeignKeyConstraintWhenMigrating: true,
		Logger: logger.New(
			log.New(os.Stderr, "\r\n", log.LstdFlags),
			logger.Config{
				LogLevel: logger.Silent,
			},
		),
	}

	if debugSQL {
		gormConfig.Logger = logger.New(
			log.New(os.Stderr, "\r\n", log.LstdFlags),
			logger.Config{
				SlowThreshold: time.Second,
				LogLevel:      logger.Info,
				Colorful:      true,
			},
		)
	}

	switch r.name {
	case dialectSqlite3:
		r.conn, err = gorm.Open(sqlite.Open(dbPath), &gormConfig)
	case dialectMysql:
		r.conn, err = gorm.Open(mysql.Open(dbPath), &gormConfig)
	case dialectPostgreSQL:
		r.conn, err = gorm.Open(postgres.Open(dbPath), &gormConfig)
	default:
		err = xerrors.Errorf("Not Supported DB dialects. r.name: %s", r.name)
	}

	if err != nil {
		if r.name == dialectSqlite3 {
			switch err.(sqlite3.Error).Code {
			case sqlite3.ErrLocked, sqlite3.ErrBusy:
				return true, xerrors.Errorf("Failed to open DB. dbtype: %s, dbpath: %s, err: %w", dbType, dbPath, err)
			}
		}
		return false, xerrors.Errorf("Failed to open DB. dbtype: %s, dbpath: %s, err: %w", dbType, dbPath, err)
	}

	if r.name == dialectSqlite3 {
		r.conn.Exec("PRAGMA foreign_keys = ON")
	}
	return false, nil
}

// MigrateDB migrates Database
func (r *RDBDriver) MigrateDB() error {
	if err := r.conn.AutoMigrate(
		&models.FetchMeta{},
		&models.Root{},
		&models.Definition{},
		&models.Package{},
		&models.Reference{},
		&models.Advisory{},
		&models.Cve{},
		&models.Bugzilla{},
		&models.Cpe{},
		&models.Debian{},
	); err != nil {
		return xerrors.Errorf("Failed to migrate. err: %w", err)
	}

	return nil
}

// CloseDB close Database
func (r *RDBDriver) CloseDB() (err error) {
	if r.conn == nil {
		return
	}

	var sqlDB *sql.DB
	if sqlDB, err = r.conn.DB(); err != nil {
		return xerrors.Errorf("Failed to get DB Object. err : %w", err)
	}
	if err = sqlDB.Close(); err != nil {
		return xerrors.Errorf("Failed to close DB. Type: %s. err: %w", r.name, err)
	}
	return
}

// GetByPackName select OVAL definition related to OS Family, osVer, packName
func (r *RDBDriver) GetByPackName(family, osVer, packName, arch string) ([]models.Definition, error) {
	family, osVer, err := formatFamilyAndOSVer(family, osVer)
	if err != nil {
		return nil, xerrors.Errorf("Failed to formatFamilyAndOSVer. err: %w", err)
	}

	q := r.conn.
		Joins("JOIN roots ON roots.id = definitions.root_id AND roots.family= ? AND roots.os_version = ?", family, osVer).
		Joins("JOIN packages ON packages.definition_id = definitions.id").
		Preload("Advisory").
		Preload("Advisory.Cves").
		Preload("Advisory.Bugzillas").
		Preload("Advisory.AffectedCPEList").
		Preload("References")

	switch family {
	case c.Debian:
		q = q.Preload("Debian").Where("packages.name = ?", packName).Preload("AffectedPacks")
	case c.Amazon, c.Oracle:
		if arch == "" {
			q = q.Where("packages.name = ?", packName).Preload("AffectedPacks")
		} else {
			q = q.Where("packages.name = ? AND packages.arch = ?", packName, arch).Preload("AffectedPacks", "arch = ?", arch)
		}
	default:
		q = q.Where("packages.name = ?", packName).Preload("AffectedPacks")
	}

	// Specify limit number to avoid `too many SQL variable`.
	// https://github.com/future-architect/vuls/issues/886
	defs := []models.Definition{}
	limit, tmpDefs := 998, []models.Definition{}
	for i := 0; true; i++ {
		err := q.
			Limit(limit).Offset(i * limit).
			Find(&tmpDefs).Error
		if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, err
		}
		if len(tmpDefs) == 0 {
			break
		}
		defs = append(defs, tmpDefs...)
	}

	if family == c.RedHat {
		for i := range defs {
			defs[i].AffectedPacks = filterByRedHatMajor(defs[i].AffectedPacks, major(osVer))
		}
	}

	return defs, nil
}

// GetByCveID select OVAL definition related to OS Family, osVer, cveID
func (r *RDBDriver) GetByCveID(family, osVer, cveID, arch string) ([]models.Definition, error) {
	family, osVer, err := formatFamilyAndOSVer(family, osVer)
	if err != nil {
		return nil, xerrors.Errorf("Failed to formatFamilyAndOSVer. err: %w", err)
	}

	q := r.conn.
		Joins("JOIN roots ON roots.id = definitions.root_id AND roots.family= ? AND roots.os_version = ?", family, osVer).
		Joins("JOIN advisories ON advisories.definition_id = definitions.id").
		Joins("JOIN cves ON cves.advisory_id = advisories.id").
		Where("cves.cve_id = ?", cveID).
		Preload("Advisory").
		Preload("Advisory.Cves").
		Preload("Advisory.Bugzillas").
		Preload("Advisory.AffectedCPEList").
		Preload("References")

	switch family {
	case c.Debian:
		q = q.Preload("Debian").Preload("AffectedPacks")
	case c.Amazon, c.Oracle:
		if arch == "" {
			q = q.Preload("AffectedPacks")
		} else {
			q = q.Preload("AffectedPacks", "arch = ?", arch)
		}
	default:
		q = q.Preload("AffectedPacks")
	}

	defs := []models.Definition{}
	if err := q.Find(&defs).Error; err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
		return nil, err
	}

	if family == c.RedHat {
		for i := range defs {
			defs[i].AffectedPacks = filterByRedHatMajor(defs[i].AffectedPacks, major(osVer))
		}
	}

	return defs, nil
}

// InsertOval inserts OVAL
func (r *RDBDriver) InsertOval(root *models.Root) error {
	family, osVer, err := formatFamilyAndOSVer(root.Family, root.OSVersion)
	if err != nil {
		return xerrors.Errorf("Failed to formatFamilyAndOSVer. err: %w", err)
	}
	log15.Info("Refreshing...", "Family", family, "Version", osVer)

	batchSize := viper.GetInt("batch-size")
	if batchSize < 1 {
		return fmt.Errorf("Failed to set batch-size. err: batch-size option is not set properly")
	}

	tx := r.conn.Begin()
	old := models.Root{}
	result := tx.Where(&models.Root{Family: family, OSVersion: osVer}).First(&old)
	if result.Error != nil && !errors.Is(result.Error, gorm.ErrRecordNotFound) {
		tx.Rollback()
		return xerrors.Errorf("Failed to select old defs: %w", result.Error)
	}

	if result.RowsAffected > 0 {
		log15.Info("Deleting old Definitions...")
		// Delete data related to root passed in arg
		defs := []models.Definition{}
		if err := tx.Model(&old).Association("Definitions").Find(&defs); err != nil {
			tx.Rollback()
			return xerrors.Errorf("Failed to select old defs: %w", err)
		}

		bar := pb.StartNew(len(defs))
		for idx := range chunkSlice(len(defs), 998) {
			var advs []models.Advisory
			if err := tx.Model(defs[idx.From:idx.To]).Association("Advisory").Find(&advs); err != nil {
				tx.Rollback()
				return xerrors.Errorf("Failed to delete: %w", err)
			}

			for idx2 := range chunkSlice(len(advs), 998) {
				if err := tx.Select(clause.Associations).Unscoped().Delete(advs[idx2.From:idx2.To]).Error; err != nil {
					tx.Rollback()
					return xerrors.Errorf("Failed to delete: %w", err)
				}
			}

			if err := tx.Select(clause.Associations).Unscoped().Delete(defs[idx.From:idx.To]).Error; err != nil {
				tx.Rollback()
				return xerrors.Errorf("Failed to delete: %w", err)
			}
			bar.Add(idx.To - idx.From)
		}
		if err := tx.Unscoped().Where("id = ?", old.ID).Delete(&models.Root{}).Error; err != nil {
			tx.Rollback()
			return xerrors.Errorf("Failed to delete: %w", err)
		}
		bar.Finish()
	}

	log15.Info("Inserting new Definitions...")
	bar := pb.StartNew(len(root.Definitions))
	if err := tx.Omit("Definitions").Create(&root).Error; err != nil {
		tx.Rollback()
		return xerrors.Errorf("Failed to insert Root. err: %w", err)
	}

	for i := range root.Definitions {
		root.Definitions[i].RootID = root.ID
	}

	for idx := range chunkSlice(len(root.Definitions), batchSize) {
		if err := tx.Create(root.Definitions[idx.From:idx.To]).Error; err != nil {
			tx.Rollback()
			return xerrors.Errorf("Failed to insert Definitions. err: %w", err)
		}
		bar.Add(idx.To - idx.From)
	}
	bar.Finish()

	return tx.Commit().Error
}

// CountDefs counts the number of definitions specified by args
func (r *RDBDriver) CountDefs(family, osVer string) (int, error) {
	family, osVer, err := formatFamilyAndOSVer(family, osVer)
	if err != nil {
		return 0, xerrors.Errorf("Failed to formatFamilyAndOSVer. err: %w", err)
	}

	root := models.Root{}
	if err := r.conn.Where(&models.Root{Family: family, OSVersion: osVer}).Take(&root).Error; err != nil {
		if !errors.Is(err, gorm.ErrRecordNotFound) {
			return 0, err
		}
		return 0, nil
	}

	var count int64
	if err := r.conn.Model(&models.Definition{}).Where(
		"root_id = ?", root.ID).Count(&count).Error; err != nil {
		return 0, err
	}
	return int(count), nil
}

// GetLastModified get last modified time of OVAL in roots
func (r *RDBDriver) GetLastModified(family, osVer string) (time.Time, error) {
	family, osVer, err := formatFamilyAndOSVer(family, osVer)
	if err != nil {
		return time.Time{}, xerrors.Errorf("Failed to formatFamilyAndOSVer. err: %w", err)
	}

	root := models.Root{}
	result := r.conn.Where(&models.Root{Family: family, OSVersion: osVer}).First(&root)
	if result.Error != nil && !errors.Is(result.Error, gorm.ErrRecordNotFound) {
		return time.Time{}, xerrors.Errorf("Failed to get root: %w", result.Error)
	}

	if result.RowsAffected == 0 {
		now := time.Now()
		return now.AddDate(-100, 0, 0), nil
	}
	return root.Timestamp, nil
}

// IsGovalDictModelV1 determines if the DB was created at the time of goval-dictionary Model v1
func (r *RDBDriver) IsGovalDictModelV1() (bool, error) {
	return r.conn.Migrator().HasColumn(&models.FetchMeta{}, "file_name"), nil
}

// GetFetchMeta get FetchMeta from Database
func (r *RDBDriver) GetFetchMeta() (fetchMeta *models.FetchMeta, err error) {
	if err = r.conn.Take(&fetchMeta).Error; err != nil {
		if !errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, err
		}
		return &models.FetchMeta{GovalDictRevision: c.Revision, SchemaVersion: models.LatestSchemaVersion, LastFetchedAt: time.Date(1000, time.January, 1, 0, 0, 0, 0, time.UTC)}, nil
	}

	return fetchMeta, nil
}

// UpsertFetchMeta upsert FetchMeta to Database
func (r *RDBDriver) UpsertFetchMeta(fetchMeta *models.FetchMeta) error {
	fetchMeta.GovalDictRevision = c.Revision
	fetchMeta.SchemaVersion = models.LatestSchemaVersion
	return r.conn.Save(fetchMeta).Error
}
<|endoftext|>


<commit_before>// Copyright 2016 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.

// Package httptrace provides mechanisms to trace the events within
// HTTP client requests.
package httptrace

import (
	"context"
	"crypto/tls"
	"internal/nettrace"
	"net"
	"reflect"
	"time"
)

// unique type to prevent assignment.
type clientEventContextKey struct{}

// ContextClientTrace returns the ClientTrace associated with the
// provided context. If none, it returns nil.
func ContextClientTrace(ctx context.Context) *ClientTrace {
	trace, _ := ctx.Value(clientEventContextKey{}).(*ClientTrace)
	return trace
}

// WithClientTrace returns a new context based on the provided parent
// ctx. HTTP client requests made with the returned context will use
// the provided trace hooks, in addition to any previous hooks
// registered with ctx. Any hooks defined in the provided trace will
// be called first.
func WithClientTrace(ctx context.Context, trace *ClientTrace) context.Context {
	if trace == nil {
		panic("nil trace")
	}
	old := ContextClientTrace(ctx)
	trace.compose(old)

	ctx = context.WithValue(ctx, clientEventContextKey{}, trace)
	if trace.hasNetHooks() {
		nt := &nettrace.Trace{
			ConnectStart: trace.ConnectStart,
			ConnectDone:  trace.ConnectDone,
		}
		if trace.DNSStart != nil {
			nt.DNSStart = func(name string) {
				trace.DNSStart(DNSStartInfo{Host: name})
			}
		}
		if trace.DNSDone != nil {
			nt.DNSDone = func(netIPs []interface{}, coalesced bool, err error) {
				addrs := make([]net.IPAddr, len(netIPs))
				for i, ip := range netIPs {
					addrs[i] = ip.(net.IPAddr)
				}
				trace.DNSDone(DNSDoneInfo{
					Addrs:     addrs,
					Coalesced: coalesced,
					Err:       err,
				})
			}
		}
		ctx = context.WithValue(ctx, nettrace.TraceKey{}, nt)
	}
	return ctx
}

// ClientTrace is a set of hooks to run at various stages of an outgoing
// HTTP request. Any particular hook may be nil. Functions may be
// called concurrently from different goroutines and some may be called
// after the request has completed or failed.
type ClientTrace struct {
	// GetConn is called before a connection is created or
	// retrieved from an idle pool. The hostPort is the
	// "host:port" of the target or proxy. GetConn is called even
	// if there's already an idle cached connection available.
	GetConn func(hostPort string)

	// GotConn is called after a successful connection is
	// obtained. There is no hook for failure to obtain a
	// connection; instead, use the error from
	// Transport.RoundTrip.
	GotConn func(GotConnInfo)

	// PutIdleConn is called when the connection is returned to
	// the idle pool. If err is nil, the connection was
	// successfully returned to the idle pool. If err is non-nil,
	// it describes why not. PutIdleConn is not called if
	// connection reuse is disabled via Transport.DisableKeepAlives.
	// PutIdleConn is called before the caller's Response.Body.Close
	// call returns.
	// For HTTP/2, this hook is not currently used.
	PutIdleConn func(err error)

	// GotFirstResponseByte is called when the first byte of the response
	// headers is available.
	GotFirstResponseByte func()

	// Got100Continue is called if the server replies with a "100
	// Continue" response.
	Got100Continue func()

	// DNSStart is called when a DNS lookup begins.
	DNSStart func(DNSStartInfo)

	// DNSDone is called when a DNS lookup ends.
	DNSDone func(DNSDoneInfo)

	// ConnectStart is called when a new connection's Dial begins.
	// If net.Dialer.DualStack (IPv6 "Happy Eyeballs") support is
	// enabled, this may be called multiple times.
	ConnectStart func(network, addr string)

	// ConnectDone is called when a new connection's Dial
	// completes. The provided err indicates whether the
	// connection completedly successfully.
	// If net.Dialer.DualStack ("Happy Eyeballs") support is
	// enabled, this may be called multiple times.
	ConnectDone func(network, addr string, err error)

	// TLSHandshakeStart is called when the TLS handshake is started. When
	// connecting to a HTTPS site via a HTTP proxy, the handshake happens after
	// the CONNECT request is processed by the proxy.
	TLSHandshakeStart func()

	// TLSHandshakeDone is called after the TLS handshake with either the
	// successful handshake's connection state, or a non-nil error on handshake
	// failure.
	TLSHandshakeDone func(tls.ConnectionState, error)

	// WroteHeaders is called after the Transport has written
	// the request headers.
	WroteHeaders func()

	// Wait100Continue is called if the Request specified
	// "Expected: 100-continue" and the Transport has written the
	// request headers but is waiting for "100 Continue" from the
	// server before writing the request body.
	Wait100Continue func()

	// WroteRequest is called with the result of writing the
	// request and any body.
	WroteRequest func(WroteRequestInfo)
}

// WroteRequestInfo contains information provided to the WroteRequest
// hook.
type WroteRequestInfo struct {
	// Err is any error encountered while writing the Request.
	Err error
}

// compose modifies t such that it respects the previously-registered hooks in old,
// subject to the composition policy requested in t.Compose.
func (t *ClientTrace) compose(old *ClientTrace) {
	if old == nil {
		return
	}
	tv := reflect.ValueOf(t).Elem()
	ov := reflect.ValueOf(old).Elem()
	structType := tv.Type()
	for i := 0; i < structType.NumField(); i++ {
		tf := tv.Field(i)
		hookType := tf.Type()
		if hookType.Kind() != reflect.Func {
			continue
		}
		of := ov.Field(i)
		if of.IsNil() {
			continue
		}
		if tf.IsNil() {
			tf.Set(of)
			continue
		}

		// Make a copy of tf for tf to call. (Otherwise it
		// creates a recursive call cycle and stack overflows)
		tfCopy := reflect.ValueOf(tf.Interface())

		// We need to call both tf and of in some order.
		newFunc := reflect.MakeFunc(hookType, func(args []reflect.Value) []reflect.Value {
			tfCopy.Call(args)
			return of.Call(args)
		})
		tv.Field(i).Set(newFunc)
	}
}

// DNSStartInfo contains information about a DNS request.
type DNSStartInfo struct {
	Host string
}

// DNSDoneInfo contains information about the results of a DNS lookup.
type DNSDoneInfo struct {
	// Addrs are the IPv4 and/or IPv6 addresses found in the DNS
	// lookup. The contents of the slice should not be mutated.
	Addrs []net.IPAddr

	// Err is any error that occurred during the DNS lookup.
	Err error

	// Coalesced is whether the Addrs were shared with another
	// caller who was doing the same DNS lookup concurrently.
	Coalesced bool
}

func (t *ClientTrace) hasNetHooks() bool {
	if t == nil {
		return false
	}
	return t.DNSStart != nil || t.DNSDone != nil || t.ConnectStart != nil || t.ConnectDone != nil
}

// GotConnInfo is the argument to the ClientTrace.GotConn function and
// contains information about the obtained connection.
type GotConnInfo struct {
	// Conn is the connection that was obtained. It is owned by
	// the http.Transport and should not be read, written or
	// closed by users of ClientTrace.
	Conn net.Conn

	// Reused is whether this connection has been previously
	// used for another HTTP request.
	Reused bool

	// WasIdle is whether this connection was obtained from an
	// idle pool.
	WasIdle bool

	// IdleTime reports how long the connection was previously
	// idle, if WasIdle is true.
	IdleTime time.Duration
}
<commit_msg>net/http/httptrace: refer http.Client users to the blog post<commit_after>// Copyright 2016 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.

// Package httptrace provides mechanisms to trace the events within
// HTTP client requests.
package httptrace

import (
	"context"
	"crypto/tls"
	"internal/nettrace"
	"net"
	"reflect"
	"time"
)

// unique type to prevent assignment.
type clientEventContextKey struct{}

// ContextClientTrace returns the ClientTrace associated with the
// provided context. If none, it returns nil.
func ContextClientTrace(ctx context.Context) *ClientTrace {
	trace, _ := ctx.Value(clientEventContextKey{}).(*ClientTrace)
	return trace
}

// WithClientTrace returns a new context based on the provided parent
// ctx. HTTP client requests made with the returned context will use
// the provided trace hooks, in addition to any previous hooks
// registered with ctx. Any hooks defined in the provided trace will
// be called first.
func WithClientTrace(ctx context.Context, trace *ClientTrace) context.Context {
	if trace == nil {
		panic("nil trace")
	}
	old := ContextClientTrace(ctx)
	trace.compose(old)

	ctx = context.WithValue(ctx, clientEventContextKey{}, trace)
	if trace.hasNetHooks() {
		nt := &nettrace.Trace{
			ConnectStart: trace.ConnectStart,
			ConnectDone:  trace.ConnectDone,
		}
		if trace.DNSStart != nil {
			nt.DNSStart = func(name string) {
				trace.DNSStart(DNSStartInfo{Host: name})
			}
		}
		if trace.DNSDone != nil {
			nt.DNSDone = func(netIPs []interface{}, coalesced bool, err error) {
				addrs := make([]net.IPAddr, len(netIPs))
				for i, ip := range netIPs {
					addrs[i] = ip.(net.IPAddr)
				}
				trace.DNSDone(DNSDoneInfo{
					Addrs:     addrs,
					Coalesced: coalesced,
					Err:       err,
				})
			}
		}
		ctx = context.WithValue(ctx, nettrace.TraceKey{}, nt)
	}
	return ctx
}

// ClientTrace is a set of hooks to run at various stages of an outgoing
// HTTP request. Any particular hook may be nil. Functions may be
// called concurrently from different goroutines and some may be called
// after the request has completed or failed.
//
// ClientTrace currently traces a single HTTP request & response
// during a single round trip and has no hooks that span a series
// of redirected requests.
//
// See https://blog.golang.org/http-tracing for more.
type ClientTrace struct {
	// GetConn is called before a connection is created or
	// retrieved from an idle pool. The hostPort is the
	// "host:port" of the target or proxy. GetConn is called even
	// if there's already an idle cached connection available.
	GetConn func(hostPort string)

	// GotConn is called after a successful connection is
	// obtained. There is no hook for failure to obtain a
	// connection; instead, use the error from
	// Transport.RoundTrip.
	GotConn func(GotConnInfo)

	// PutIdleConn is called when the connection is returned to
	// the idle pool. If err is nil, the connection was
	// successfully returned to the idle pool. If err is non-nil,
	// it describes why not. PutIdleConn is not called if
	// connection reuse is disabled via Transport.DisableKeepAlives.
	// PutIdleConn is called before the caller's Response.Body.Close
	// call returns.
	// For HTTP/2, this hook is not currently used.
	PutIdleConn func(err error)

	// GotFirstResponseByte is called when the first byte of the response
	// headers is available.
	GotFirstResponseByte func()

	// Got100Continue is called if the server replies with a "100
	// Continue" response.
	Got100Continue func()

	// DNSStart is called when a DNS lookup begins.
	DNSStart func(DNSStartInfo)

	// DNSDone is called when a DNS lookup ends.
	DNSDone func(DNSDoneInfo)

	// ConnectStart is called when a new connection's Dial begins.
	// If net.Dialer.DualStack (IPv6 "Happy Eyeballs") support is
	// enabled, this may be called multiple times.
	ConnectStart func(network, addr string)

	// ConnectDone is called when a new connection's Dial
	// completes. The provided err indicates whether the
	// connection completedly successfully.
	// If net.Dialer.DualStack ("Happy Eyeballs") support is
	// enabled, this may be called multiple times.
	ConnectDone func(network, addr string, err error)

	// TLSHandshakeStart is called when the TLS handshake is started. When
	// connecting to a HTTPS site via a HTTP proxy, the handshake happens after
	// the CONNECT request is processed by the proxy.
	TLSHandshakeStart func()

	// TLSHandshakeDone is called after the TLS handshake with either the
	// successful handshake's connection state, or a non-nil error on handshake
	// failure.
	TLSHandshakeDone func(tls.ConnectionState, error)

	// WroteHeaders is called after the Transport has written
	// the request headers.
	WroteHeaders func()

	// Wait100Continue is called if the Request specified
	// "Expected: 100-continue" and the Transport has written the
	// request headers but is waiting for "100 Continue" from the
	// server before writing the request body.
	Wait100Continue func()

	// WroteRequest is called with the result of writing the
	// request and any body.
	WroteRequest func(WroteRequestInfo)
}

// WroteRequestInfo contains information provided to the WroteRequest
// hook.
type WroteRequestInfo struct {
	// Err is any error encountered while writing the Request.
	Err error
}

// compose modifies t such that it respects the previously-registered hooks in old,
// subject to the composition policy requested in t.Compose.
func (t *ClientTrace) compose(old *ClientTrace) {
	if old == nil {
		return
	}
	tv := reflect.ValueOf(t).Elem()
	ov := reflect.ValueOf(old).Elem()
	structType := tv.Type()
	for i := 0; i < structType.NumField(); i++ {
		tf := tv.Field(i)
		hookType := tf.Type()
		if hookType.Kind() != reflect.Func {
			continue
		}
		of := ov.Field(i)
		if of.IsNil() {
			continue
		}
		if tf.IsNil() {
			tf.Set(of)
			continue
		}

		// Make a copy of tf for tf to call. (Otherwise it
		// creates a recursive call cycle and stack overflows)
		tfCopy := reflect.ValueOf(tf.Interface())

		// We need to call both tf and of in some order.
		newFunc := reflect.MakeFunc(hookType, func(args []reflect.Value) []reflect.Value {
			tfCopy.Call(args)
			return of.Call(args)
		})
		tv.Field(i).Set(newFunc)
	}
}

// DNSStartInfo contains information about a DNS request.
type DNSStartInfo struct {
	Host string
}

// DNSDoneInfo contains information about the results of a DNS lookup.
type DNSDoneInfo struct {
	// Addrs are the IPv4 and/or IPv6 addresses found in the DNS
	// lookup. The contents of the slice should not be mutated.
	Addrs []net.IPAddr

	// Err is any error that occurred during the DNS lookup.
	Err error

	// Coalesced is whether the Addrs were shared with another
	// caller who was doing the same DNS lookup concurrently.
	Coalesced bool
}

func (t *ClientTrace) hasNetHooks() bool {
	if t == nil {
		return false
	}
	return t.DNSStart != nil || t.DNSDone != nil || t.ConnectStart != nil || t.ConnectDone != nil
}

// GotConnInfo is the argument to the ClientTrace.GotConn function and
// contains information about the obtained connection.
type GotConnInfo struct {
	// Conn is the connection that was obtained. It is owned by
	// the http.Transport and should not be read, written or
	// closed by users of ClientTrace.
	Conn net.Conn

	// Reused is whether this connection has been previously
	// used for another HTTP request.
	Reused bool

	// WasIdle is whether this connection was obtained from an
	// idle pool.
	WasIdle bool

	// IdleTime reports how long the connection was previously
	// idle, if WasIdle is true.
	IdleTime time.Duration
}
<|endoftext|>