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

import (
	"fmt"
	"net"
	"unsafe"

	"github.com/cilium/cilium/pkg/bpf"
)

const (
	// MaxEntries is the maximum number of keys that can be present in the
	// RemoteEndpointMap.
	MaxEntries = 512000
)

// EndpointKey implements the bpf.MapKey interface.
type EndpointKey struct {
	bpf.EndpointKey
}

// GetKeyPtr returns the unsafe pointer to the BPF key
func (k EndpointKey) GetKeyPtr() unsafe.Pointer { return unsafe.Pointer(&k) }

// NewValue returns a new empty instance of the structure representing the BPF
// map value
func (k EndpointKey) NewValue() bpf.MapValue { return &RemoteEndpointInfo{} }

// NewEndpointKey returns an EndpointKey based on the provided IP address. The
// address family is automatically detected
func NewEndpointKey(ip net.IP) EndpointKey {
	return EndpointKey{
		EndpointKey: bpf.NewEndpointKey(ip),
	}
}

// RemoteEndpointInfo implements the bpf.MapValue interface. It contains the
// security identity of a remote endpoint.
type RemoteEndpointInfo struct {
	SecurityIdentity uint16
	Pad              [3]uint16
}

func (v RemoteEndpointInfo) String() string {
	return fmt.Sprintf("%d", v.SecurityIdentity)
}

// GetValuePtr returns the unsafe pointer to the BPF value.
func (v RemoteEndpointInfo) GetValuePtr() unsafe.Pointer { return unsafe.Pointer(&v) }

var (
	// IPCache is a mapping of all endpoint IPs in the cluster which this
	// Cilium agent is a part of to their corresponding security identities.
	// It is a singleton; there is only one such map per agent.
	IPCache = bpf.NewMap(
		"cilium_ipcache",
		bpf.BPF_MAP_TYPE_HASH,
		int(unsafe.Sizeof(EndpointKey{})),
		int(unsafe.Sizeof(RemoteEndpointInfo{})),
		MaxEntries,
		0,
		func(key []byte, value []byte) (bpf.MapKey, bpf.MapValue, error) {
			k, v := EndpointKey{}, RemoteEndpointInfo{}

			if err := bpf.ConvertKeyValue(key, value, &k, &v); err != nil {
				return nil, nil, err
			}
			return k, v, nil
		},
	)
)

func init() {
	bpf.OpenAfterMount(IPCache)
}
<commit_msg>pkg/maps/ipcache: log if map unable to be opened<commit_after>// Copyright 2016-2018 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 ipcache

import (
	"fmt"
	"net"
	"unsafe"

	"github.com/cilium/cilium/pkg/bpf"
	"github.com/cilium/cilium/pkg/logging"
)

var log = logging.DefaultLogger

const (
	// MaxEntries is the maximum number of keys that can be present in the
	// RemoteEndpointMap.
	MaxEntries = 512000
)

// EndpointKey implements the bpf.MapKey interface.
type EndpointKey struct {
	bpf.EndpointKey
}

// GetKeyPtr returns the unsafe pointer to the BPF key
func (k EndpointKey) GetKeyPtr() unsafe.Pointer { return unsafe.Pointer(&k) }

// NewValue returns a new empty instance of the structure representing the BPF
// map value
func (k EndpointKey) NewValue() bpf.MapValue { return &RemoteEndpointInfo{} }

// NewEndpointKey returns an EndpointKey based on the provided IP address. The
// address family is automatically detected
func NewEndpointKey(ip net.IP) EndpointKey {
	return EndpointKey{
		EndpointKey: bpf.NewEndpointKey(ip),
	}
}

// RemoteEndpointInfo implements the bpf.MapValue interface. It contains the
// security identity of a remote endpoint.
type RemoteEndpointInfo struct {
	SecurityIdentity uint16
	Pad              [3]uint16
}

func (v RemoteEndpointInfo) String() string {
	return fmt.Sprintf("%d", v.SecurityIdentity)
}

// GetValuePtr returns the unsafe pointer to the BPF value.
func (v RemoteEndpointInfo) GetValuePtr() unsafe.Pointer { return unsafe.Pointer(&v) }

var (
	// IPCache is a mapping of all endpoint IPs in the cluster which this
	// Cilium agent is a part of to their corresponding security identities.
	// It is a singleton; there is only one such map per agent.
	IPCache = bpf.NewMap(
		"cilium_ipcache",
		bpf.BPF_MAP_TYPE_HASH,
		int(unsafe.Sizeof(EndpointKey{})),
		int(unsafe.Sizeof(RemoteEndpointInfo{})),
		MaxEntries,
		0,
		func(key []byte, value []byte) (bpf.MapKey, bpf.MapValue, error) {
			k, v := EndpointKey{}, RemoteEndpointInfo{}

			if err := bpf.ConvertKeyValue(key, value, &k, &v); err != nil {
				return nil, nil, err
			}
			return k, v, nil
		},
	)
)

func init() {
	err := bpf.OpenAfterMount(IPCache)
	if err != nil {
		log.WithError(err).Error("unable to open map")
	}
}
<|endoftext|>


<commit_before>package notification

import (
	"sync"

	"github.com/TimothyYe/godns/internal/settings"
	"github.com/TimothyYe/godns/pkg/lib"

	log "github.com/sirupsen/logrus"
)

const (
	Email    = "email"
	Slack    = "slack"
	Telegram = "telegram"
	Discord  = "discord"
	Pushover = "pushover"
)

var (
	instance *notificationManager
	once     sync.Once
)

type INotification interface {
	Send(domain, currentIP string) error
}

type INotificationManager interface {
	Send(string, string)
}

type notificationManager struct {
	notifications map[string]INotification
}

func GetNotificationManager(conf *settings.Settings) INotificationManager {
	once.Do(func() {
		instance = &notificationManager{
			notifications: initNotifications(conf),
		}
	})

	return instance
}

func initNotifications(conf *settings.Settings) map[string]INotification {
	notificationMap := map[string]INotification{}

	if conf.Notify.Mail.Enabled {
		notificationMap[Email] = NewEmailNotification(conf)
	}

	if conf.Notify.Slack.Enabled {
		notificationMap[Slack] = NewSlackNotification(conf)
	}

	if conf.Notify.Telegram.Enabled {
		notificationMap[Telegram] = NewTelegramNotification(conf)
	}

	if conf.Notify.Pushover.Enabled {
		notificationMap[Pushover] = NewPushoverNotification(conf)
	}

	if conf.Notify.Discord.Enabled {
		notificationMap[Discord] = NewDiscordNotification(conf)
	}

	return notificationMap
}

func (n *notificationManager) Send(domain, currentIP string) {
	for _, sender := range n.notifications {
		lib.SafeGo(func() {
			if err := sender.Send(domain, currentIP); err != nil {
				log.Error("Send notification with error:", err)
			}
		})
	}
}
<commit_msg>fix run once issue (#173)<commit_after>package notification

import (
	"sync"

	"github.com/TimothyYe/godns/internal/settings"

	log "github.com/sirupsen/logrus"
)

const (
	Email    = "email"
	Slack    = "slack"
	Telegram = "telegram"
	Discord  = "discord"
	Pushover = "pushover"
)

var (
	instance *notificationManager
	once     sync.Once
)

type INotification interface {
	Send(domain, currentIP string) error
}

type INotificationManager interface {
	Send(string, string)
}

type notificationManager struct {
	notifications map[string]INotification
}

func GetNotificationManager(conf *settings.Settings) INotificationManager {
	once.Do(func() {
		instance = &notificationManager{
			notifications: initNotifications(conf),
		}
	})

	return instance
}

func initNotifications(conf *settings.Settings) map[string]INotification {
	notificationMap := map[string]INotification{}

	if conf.Notify.Mail.Enabled {
		notificationMap[Email] = NewEmailNotification(conf)
	}

	if conf.Notify.Slack.Enabled {
		notificationMap[Slack] = NewSlackNotification(conf)
	}

	if conf.Notify.Telegram.Enabled {
		notificationMap[Telegram] = NewTelegramNotification(conf)
	}

	if conf.Notify.Pushover.Enabled {
		notificationMap[Pushover] = NewPushoverNotification(conf)
	}

	if conf.Notify.Discord.Enabled {
		notificationMap[Discord] = NewDiscordNotification(conf)
	}

	return notificationMap
}

func (n *notificationManager) Send(domain, currentIP string) {
	for _, sender := range n.notifications {
		if err := sender.Send(domain, currentIP); err != nil {
			log.Error("Send notification with error:", err)
		}
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2021 The cert-manager 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 approver

import (
	"context"

	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	apiutil "github.com/jetstack/cert-manager/pkg/api/util"
	cmapi "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1"
	cmmeta "github.com/jetstack/cert-manager/pkg/apis/meta/v1"
	logf "github.com/jetstack/cert-manager/pkg/logs"
)

const (
	ApprovedMessage = "Certificate request has been approved by cert-manager.io"
)

// Sync will set the "Approved" condition to True on synced
// CertificateRequests. If the "Denied", "Approved" or "Ready" condition
// already exists, exit early.
func (c *Controller) Sync(ctx context.Context, cr *cmapi.CertificateRequest) (err error) {
	log := logf.FromContext(ctx, "approver")

	switch {
	case
		// If the CertificateRequest has already been approved, exit early.
		apiutil.CertificateRequestIsApproved(cr),

		// If the CertificateRequest has already been denied, exit early.
		apiutil.CertificateRequestIsDenied(cr),

		// If the CertificateRequest is "Issued" or "Failed", exit early.
		apiutil.CertificateRequestReadyReason(cr) == cmapi.CertificateRequestReasonFailed,
		apiutil.CertificateRequestReadyReason(cr) == cmapi.CertificateRequestReasonIssued:
		return nil
	}

	// Update the CertificateRequest approved condition to true.
	apiutil.SetCertificateRequestCondition(cr,
		cmapi.CertificateRequestConditionApproved,
		cmmeta.ConditionTrue,
		"cert-manager.io",
		ApprovedMessage,
	)

	// Update CertificateRequest with
	_, err = c.cmClient.CertmanagerV1().CertificateRequests(cr.Namespace).UpdateStatus(ctx, cr, metav1.UpdateOptions{})
	if err != nil {
		return err
	}
	c.recorder.Event(cr, corev1.EventTypeNormal, "cert-manager.io", ApprovedMessage)

	log.V(logf.DebugLevel).Info("approved certificate request")

	return nil
}
<commit_msg>data race: fix certificate requests in cache being mutated<commit_after>/*
Copyright 2021 The cert-manager 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 approver

import (
	"context"

	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

	apiutil "github.com/jetstack/cert-manager/pkg/api/util"
	cmapi "github.com/jetstack/cert-manager/pkg/apis/certmanager/v1"
	cmmeta "github.com/jetstack/cert-manager/pkg/apis/meta/v1"
	logf "github.com/jetstack/cert-manager/pkg/logs"
)

const (
	ApprovedMessage = "Certificate request has been approved by cert-manager.io"
)

// Sync will set the "Approved" condition to True on synced
// CertificateRequests. If the "Denied", "Approved" or "Ready" condition
// already exists, exit early.
func (c *Controller) Sync(ctx context.Context, cr *cmapi.CertificateRequest) (err error) {
	log := logf.FromContext(ctx, "approver")

	switch {
	case
		// If the CertificateRequest has already been approved, exit early.
		apiutil.CertificateRequestIsApproved(cr),

		// If the CertificateRequest has already been denied, exit early.
		apiutil.CertificateRequestIsDenied(cr),

		// If the CertificateRequest is "Issued" or "Failed", exit early.
		apiutil.CertificateRequestReadyReason(cr) == cmapi.CertificateRequestReasonFailed,
		apiutil.CertificateRequestReadyReason(cr) == cmapi.CertificateRequestReasonIssued:
		return nil
	}

	// Update the CertificateRequest approved condition to true.
	cr = cr.DeepCopy()
	apiutil.SetCertificateRequestCondition(cr,
		cmapi.CertificateRequestConditionApproved,
		cmmeta.ConditionTrue,
		"cert-manager.io",
		ApprovedMessage,
	)

	// Update CertificateRequest with
	_, err = c.cmClient.CertmanagerV1().CertificateRequests(cr.Namespace).UpdateStatus(ctx, cr, metav1.UpdateOptions{})
	if err != nil {
		return err
	}
	c.recorder.Event(cr, corev1.EventTypeNormal, "cert-manager.io", ApprovedMessage)

	log.V(logf.DebugLevel).Info("approved certificate request")

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"log"
	"time"

	"github.com/tmrts/flamingo/pkg/datasrc"
	"github.com/tmrts/flamingo/pkg/distro"
	"github.com/tmrts/flamingo/pkg/sys"
)

var flags struct {
	cloudConfig string
	configDrive string
	metadata    string
}

func init() {
	flag.StringVar(&flags.cloudConfig, "cloud-config", "", "user-data configuration file")
	flag.StringVar(&flags.configDrive, "config-drive", "", "config drive mount path")
	flag.StringVar(&flags.metadata, "meta-data", "", "meta-data file")
}

func InitializeContextualization() error {
	// TODO(tmrts): Add plug-in hooks
	log.Print("main: starting the contextualization")
	return nil
}

func FinalizeContextualization() error {
	// TODO(tmrts): Add plug-in hooks
	log.Print("main: finalizing the contextualization")
	return nil
}

func HasRootPrivileges() bool {
	// TODO(tmrts): Move privilege check to sys.nss package
	ent, err := sys.Execute("getent", "gshadow", "root")
	if err != nil {
		return false, err
	}

	return ent != "", nil
}

func main() {
	flag.Parse()

	/*
	 *    if flags.cloudConfig != "" {
	 *        conf, err := os.Open(flags.cloudConfig)
	 *        if err != nil {
	 *            log.Panicf("fatal error config file: %v", err)
	 *        }
	 *
	 *        _, err = cloudconfig.Parse(conf)
	 *        if err != nil {
	 *            log.Panicf("fatal error config file: %v", err)
	 *        }
	 *    }
	 */

	// TODO(tmrts): Build a logger hierarchy
	hasRoot, err := HasRootPrivileges()
	if err != nil {
		log.Fatalf("failed to check current user privileges, %v", err)
	}

	if !hasRoot {
		log.Fatal("current user doesn't have root privileges")
	}

	centOS := distro.CentOS(sys.DefaultExecutor)

	if err := InitializeContextualization(); err != nil {
		log.Fatalf("failed to contextualization, %v", err)
	}

	providers := datasrc.SupportedProviders()

	p, err := datasrc.FindProvider(providers, 5*time.Second)
	if err != nil {
		log.Fatalf("failed to find an available datasource provider, %v", err)
	}

	m, err := p.FetchMetadata()
	if err != nil {
		log.Fatalf("failed to fetch meta-data from provider %v, %v", p, err)
	}

	u, err := p.FetchUserdata()
	if err != nil {
		log.Fatalf("failed to fetch user-data from provider %v, %v", p, err)
	}

	if err := centOS.ConsumeMetadata(m); err != nil {
		log.Fatalf("failed to consume meta-data, %v", err)
	}

	if err := centOS.ConsumeUserdata(u); err != nil {
		log.Fatalf("failed to consume user-data, %v", err)
	}

	if err := FinalizeContextualization(); err != nil {
		log.Fatalf("failed to finalize contextualization, %v", err)
	}
}
<commit_msg>Implement root privilege checks<commit_after>package main

import (
	"flag"
	"log"
	"time"

	"github.com/tmrts/flamingo/pkg/datasrc"
	"github.com/tmrts/flamingo/pkg/distro"
	"github.com/tmrts/flamingo/pkg/sys"
)

var flags struct {
	cloudConfig string
	configDrive string
	metadata    string
}

func init() {
	flag.StringVar(&flags.cloudConfig, "cloud-config", "", "user-data configuration file")
	flag.StringVar(&flags.configDrive, "config-drive", "", "config drive mount path")
	flag.StringVar(&flags.metadata, "meta-data", "", "meta-data file")
}

func InitializeContextualization() error {
	// TODO(tmrts): Add plug-in hooks
	log.Print("main: starting the contextualization")
	return nil
}

func FinalizeContextualization() error {
	// TODO(tmrts): Add plug-in hooks
	log.Print("main: finalizing the contextualization")
	return nil
}

func HasRootPrivileges() (bool, error) {
	// TODO(tmrts): Move privilege check to sys.nss package
	ent, err := sys.Execute("getent", "gshadow", "root")
	if err != nil {
		return false, err
	}

	return ent != "", nil
}

func main() {
	flag.Parse()

	/*
	 *    if flags.cloudConfig != "" {
	 *        conf, err := os.Open(flags.cloudConfig)
	 *        if err != nil {
	 *            log.Panicf("fatal error config file: %v", err)
	 *        }
	 *
	 *        _, err = cloudconfig.Parse(conf)
	 *        if err != nil {
	 *            log.Panicf("fatal error config file: %v", err)
	 *        }
	 *    }
	 */

	// TODO(tmrts): Build a logger hierarchy
	hasRoot, err := HasRootPrivileges()
	if err != nil {
		log.Fatalf("failed to check current user privileges, %v", err)
	}

	if !hasRoot {
		log.Fatal("current user doesn't have root privileges")
	}

	centOS := distro.CentOS(sys.DefaultExecutor)

	if err := InitializeContextualization(); err != nil {
		log.Fatalf("failed to contextualization, %v", err)
	}

	providers := datasrc.SupportedProviders()

	p, err := datasrc.FindProvider(providers, 5*time.Second)
	if err != nil {
		log.Fatalf("failed to find an available datasource provider, %v", err)
	}

	m, err := p.FetchMetadata()
	if err != nil {
		log.Fatalf("failed to fetch meta-data from provider %v, %v", p, err)
	}

	u, err := p.FetchUserdata()
	if err != nil {
		log.Fatalf("failed to fetch user-data from provider %v, %v", p, err)
	}

	if err := centOS.ConsumeMetadata(m); err != nil {
		log.Fatalf("failed to consume meta-data, %v", err)
	}

	if err := centOS.ConsumeUserdata(u); err != nil {
		log.Fatalf("failed to consume user-data, %v", err)
	}

	if err := FinalizeContextualization(); err != nil {
		log.Fatalf("failed to finalize contextualization, %v", err)
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Kubernetes Authors.

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

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

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

package fieldmanager

import (
	"fmt"
	"reflect"
	"time"

	"k8s.io/apimachinery/pkg/api/meta"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal"
	"k8s.io/klog/v2"
	"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
	"sigs.k8s.io/structured-merge-diff/v4/merge"
)

// DefaultMaxUpdateManagers defines the default maximum retained number of managedFields entries from updates
// if the number of update managers exceeds this, the oldest entries will be merged until the number is below the maximum.
// TODO(jennybuckley): Determine if this is really the best value. Ideally we wouldn't unnecessarily merge too many entries.
const DefaultMaxUpdateManagers int = 10

// DefaultTrackOnCreateProbability defines the default probability that the field management of an object
// starts being tracked from the object's creation, instead of from the first time the object is applied to.
const DefaultTrackOnCreateProbability float32 = 1

var atMostEverySecond = internal.NewAtMostEvery(time.Second)

// Managed groups a fieldpath.ManagedFields together with the timestamps associated with each operation.
type Managed interface {
	// Fields gets the fieldpath.ManagedFields.
	Fields() fieldpath.ManagedFields

	// Times gets the timestamps associated with each operation.
	Times() map[string]*metav1.Time
}

// Manager updates the managed fields and merges applied configurations.
type Manager interface {
	// Update is used when the object has already been merged (non-apply
	// use-case), and simply updates the managed fields in the output
	// object.
	Update(liveObj, newObj runtime.Object, managed Managed, manager string) (runtime.Object, Managed, error)

	// Apply is used when server-side apply is called, as it merges the
	// object and updates the managed fields.
	Apply(liveObj, appliedObj runtime.Object, managed Managed, fieldManager string, force bool) (runtime.Object, Managed, error)
}

// FieldManager updates the managed fields and merge applied
// configurations.
type FieldManager struct {
	fieldManager Manager
	subresource  string
}

// NewFieldManager creates a new FieldManager that decodes, manages, then re-encodes managedFields
// on update and apply requests.
func NewFieldManager(f Manager, subresource string) *FieldManager {
	return &FieldManager{fieldManager: f, subresource: subresource}
}

// NewDefaultFieldManager creates a new FieldManager that merges apply requests
// and update managed fields for other types of requests.
func NewDefaultFieldManager(typeConverter TypeConverter, objectConverter runtime.ObjectConvertor, objectDefaulter runtime.ObjectDefaulter, objectCreater runtime.ObjectCreater, kind schema.GroupVersionKind, hub schema.GroupVersion, subresource string, resetFields map[fieldpath.APIVersion]*fieldpath.Set) (*FieldManager, error) {
	f, err := NewStructuredMergeManager(typeConverter, objectConverter, objectDefaulter, kind.GroupVersion(), hub, resetFields)
	if err != nil {
		return nil, fmt.Errorf("failed to create field manager: %v", err)
	}
	return newDefaultFieldManager(f, typeConverter, objectConverter, objectCreater, kind, subresource), nil
}

// NewDefaultCRDFieldManager creates a new FieldManager specifically for
// CRDs. This allows for the possibility of fields which are not defined
// in models, as well as having no models defined at all.
func NewDefaultCRDFieldManager(typeConverter TypeConverter, objectConverter runtime.ObjectConvertor, objectDefaulter runtime.ObjectDefaulter, objectCreater runtime.ObjectCreater, kind schema.GroupVersionKind, hub schema.GroupVersion, subresource string, resetFields map[fieldpath.APIVersion]*fieldpath.Set) (_ *FieldManager, err error) {
	f, err := NewCRDStructuredMergeManager(typeConverter, objectConverter, objectDefaulter, kind.GroupVersion(), hub, resetFields)
	if err != nil {
		return nil, fmt.Errorf("failed to create field manager: %v", err)
	}
	return newDefaultFieldManager(f, typeConverter, objectConverter, objectCreater, kind, subresource), nil
}

// newDefaultFieldManager is a helper function which wraps a Manager with certain default logic.
func newDefaultFieldManager(f Manager, typeConverter TypeConverter, objectConverter runtime.ObjectConvertor, objectCreater runtime.ObjectCreater, kind schema.GroupVersionKind, subresource string) *FieldManager {
	return NewFieldManager(
		NewLastAppliedUpdater(
			NewLastAppliedManager(
				NewProbabilisticSkipNonAppliedManager(
					NewCapManagersManager(
						NewBuildManagerInfoManager(
							NewManagedFieldsUpdater(
								NewStripMetaManager(f),
							), kind.GroupVersion(), subresource,
						), DefaultMaxUpdateManagers,
					), objectCreater, kind, DefaultTrackOnCreateProbability,
				), typeConverter, objectConverter, kind.GroupVersion()),
		), subresource,
	)
}

// DecodeManagedFields converts ManagedFields from the wire format (api format)
// to the format used by sigs.k8s.io/structured-merge-diff
func DecodeManagedFields(encodedManagedFields []metav1.ManagedFieldsEntry) (Managed, error) {
	return internal.DecodeManagedFields(encodedManagedFields)
}

func decodeLiveOrNew(liveObj, newObj runtime.Object, ignoreManagedFieldsFromRequestObject bool) (Managed, error) {
	liveAccessor, err := meta.Accessor(liveObj)
	if err != nil {
		return nil, err
	}

	// We take the managedFields of the live object in case the request tries to
	// manually set managedFields via a subresource.
	if ignoreManagedFieldsFromRequestObject {
		return emptyManagedFieldsOnErr(DecodeManagedFields(liveAccessor.GetManagedFields()))
	}

	// If the object doesn't have metadata, we should just return without trying to
	// set the managedFields at all, so creates/updates/patches will work normally.
	newAccessor, err := meta.Accessor(newObj)
	if err != nil {
		return nil, err
	}

	if isResetManagedFields(newAccessor.GetManagedFields()) {
		return internal.NewEmptyManaged(), nil
	}

	// If the managed field is empty or we failed to decode it,
	// let's try the live object. This is to prevent clients who
	// don't understand managedFields from deleting it accidentally.
	managed, err := DecodeManagedFields(newAccessor.GetManagedFields())
	if err != nil || len(managed.Fields()) == 0 {
		return emptyManagedFieldsOnErr(DecodeManagedFields(liveAccessor.GetManagedFields()))
	}
	return managed, nil
}

func emptyManagedFieldsOnErr(managed Managed, err error) (Managed, error) {
	if err != nil {
		return internal.NewEmptyManaged(), nil
	}
	return managed, nil
}

// Update is used when the object has already been merged (non-apply
// use-case), and simply updates the managed fields in the output
// object.
func (f *FieldManager) Update(liveObj, newObj runtime.Object, manager string) (object runtime.Object, err error) {
	// First try to decode the managed fields provided in the update,
	// This is necessary to allow directly updating managed fields.
	isSubresource := f.subresource != ""
	managed, err := decodeLiveOrNew(liveObj, newObj, isSubresource)
	if err != nil {
		return newObj, nil
	}

	internal.RemoveObjectManagedFields(liveObj)
	internal.RemoveObjectManagedFields(newObj)

	if object, managed, err = f.fieldManager.Update(liveObj, newObj, managed, manager); err != nil {
		return nil, err
	}

	if err = internal.EncodeObjectManagedFields(object, managed); err != nil {
		return nil, fmt.Errorf("failed to encode managed fields: %v", err)
	}

	return object, nil
}

// UpdateNoErrors is the same as Update, but it will not return
// errors. If an error happens, the object is returned with
// managedFields cleared.
func (f *FieldManager) UpdateNoErrors(liveObj, newObj runtime.Object, manager string) runtime.Object {
	obj, err := f.Update(liveObj, newObj, manager)
	if err != nil {
		atMostEverySecond.Do(func() {
			klog.ErrorS(err, "[SHOULD NOT HAPPEN] failed to update managedFields", "VersionKind",
				newObj.GetObjectKind().GroupVersionKind())
		})
		// Explicitly remove managedFields on failure, so that
		// we can't have garbage in it.
		internal.RemoveObjectManagedFields(newObj)
		return newObj
	}
	return obj
}

// Returns true if the managedFields indicate that the user is trying to
// reset the managedFields, i.e. if the list is non-nil but empty, or if
// the list has one empty item.
func isResetManagedFields(managedFields []metav1.ManagedFieldsEntry) bool {
	if len(managedFields) == 0 {
		return managedFields != nil
	}

	if len(managedFields) == 1 {
		return reflect.DeepEqual(managedFields[0], metav1.ManagedFieldsEntry{})
	}

	return false
}

// Apply is used when server-side apply is called, as it merges the
// object and updates the managed fields.
func (f *FieldManager) Apply(liveObj, appliedObj runtime.Object, manager string, force bool) (object runtime.Object, err error) {
	// If the object doesn't have metadata, apply isn't allowed.
	accessor, err := meta.Accessor(liveObj)
	if err != nil {
		return nil, fmt.Errorf("couldn't get accessor: %v", err)
	}

	// Decode the managed fields in the live object, since it isn't allowed in the patch.
	managed, err := DecodeManagedFields(accessor.GetManagedFields())
	if err != nil {
		return nil, fmt.Errorf("failed to decode managed fields: %v", err)
	}

	internal.RemoveObjectManagedFields(liveObj)

	object, managed, err = f.fieldManager.Apply(liveObj, appliedObj, managed, manager, force)
	if err != nil {
		if conflicts, ok := err.(merge.Conflicts); ok {
			return nil, internal.NewConflictError(conflicts)
		}
		return nil, err
	}

	if err = internal.EncodeObjectManagedFields(object, managed); err != nil {
		return nil, fmt.Errorf("failed to encode managed fields: %v", err)
	}

	return object, nil
}
<commit_msg>server-side-apply: print object name in SHOULD NOT HAPPEN message<commit_after>/*
Copyright 2018 The Kubernetes Authors.

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

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

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

package fieldmanager

import (
	"fmt"
	"reflect"
	"time"

	"k8s.io/apimachinery/pkg/api/meta"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal"
	"k8s.io/klog/v2"
	"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
	"sigs.k8s.io/structured-merge-diff/v4/merge"
)

// DefaultMaxUpdateManagers defines the default maximum retained number of managedFields entries from updates
// if the number of update managers exceeds this, the oldest entries will be merged until the number is below the maximum.
// TODO(jennybuckley): Determine if this is really the best value. Ideally we wouldn't unnecessarily merge too many entries.
const DefaultMaxUpdateManagers int = 10

// DefaultTrackOnCreateProbability defines the default probability that the field management of an object
// starts being tracked from the object's creation, instead of from the first time the object is applied to.
const DefaultTrackOnCreateProbability float32 = 1

var atMostEverySecond = internal.NewAtMostEvery(time.Second)

// Managed groups a fieldpath.ManagedFields together with the timestamps associated with each operation.
type Managed interface {
	// Fields gets the fieldpath.ManagedFields.
	Fields() fieldpath.ManagedFields

	// Times gets the timestamps associated with each operation.
	Times() map[string]*metav1.Time
}

// Manager updates the managed fields and merges applied configurations.
type Manager interface {
	// Update is used when the object has already been merged (non-apply
	// use-case), and simply updates the managed fields in the output
	// object.
	Update(liveObj, newObj runtime.Object, managed Managed, manager string) (runtime.Object, Managed, error)

	// Apply is used when server-side apply is called, as it merges the
	// object and updates the managed fields.
	Apply(liveObj, appliedObj runtime.Object, managed Managed, fieldManager string, force bool) (runtime.Object, Managed, error)
}

// FieldManager updates the managed fields and merge applied
// configurations.
type FieldManager struct {
	fieldManager Manager
	subresource  string
}

// NewFieldManager creates a new FieldManager that decodes, manages, then re-encodes managedFields
// on update and apply requests.
func NewFieldManager(f Manager, subresource string) *FieldManager {
	return &FieldManager{fieldManager: f, subresource: subresource}
}

// NewDefaultFieldManager creates a new FieldManager that merges apply requests
// and update managed fields for other types of requests.
func NewDefaultFieldManager(typeConverter TypeConverter, objectConverter runtime.ObjectConvertor, objectDefaulter runtime.ObjectDefaulter, objectCreater runtime.ObjectCreater, kind schema.GroupVersionKind, hub schema.GroupVersion, subresource string, resetFields map[fieldpath.APIVersion]*fieldpath.Set) (*FieldManager, error) {
	f, err := NewStructuredMergeManager(typeConverter, objectConverter, objectDefaulter, kind.GroupVersion(), hub, resetFields)
	if err != nil {
		return nil, fmt.Errorf("failed to create field manager: %v", err)
	}
	return newDefaultFieldManager(f, typeConverter, objectConverter, objectCreater, kind, subresource), nil
}

// NewDefaultCRDFieldManager creates a new FieldManager specifically for
// CRDs. This allows for the possibility of fields which are not defined
// in models, as well as having no models defined at all.
func NewDefaultCRDFieldManager(typeConverter TypeConverter, objectConverter runtime.ObjectConvertor, objectDefaulter runtime.ObjectDefaulter, objectCreater runtime.ObjectCreater, kind schema.GroupVersionKind, hub schema.GroupVersion, subresource string, resetFields map[fieldpath.APIVersion]*fieldpath.Set) (_ *FieldManager, err error) {
	f, err := NewCRDStructuredMergeManager(typeConverter, objectConverter, objectDefaulter, kind.GroupVersion(), hub, resetFields)
	if err != nil {
		return nil, fmt.Errorf("failed to create field manager: %v", err)
	}
	return newDefaultFieldManager(f, typeConverter, objectConverter, objectCreater, kind, subresource), nil
}

// newDefaultFieldManager is a helper function which wraps a Manager with certain default logic.
func newDefaultFieldManager(f Manager, typeConverter TypeConverter, objectConverter runtime.ObjectConvertor, objectCreater runtime.ObjectCreater, kind schema.GroupVersionKind, subresource string) *FieldManager {
	return NewFieldManager(
		NewLastAppliedUpdater(
			NewLastAppliedManager(
				NewProbabilisticSkipNonAppliedManager(
					NewCapManagersManager(
						NewBuildManagerInfoManager(
							NewManagedFieldsUpdater(
								NewStripMetaManager(f),
							), kind.GroupVersion(), subresource,
						), DefaultMaxUpdateManagers,
					), objectCreater, kind, DefaultTrackOnCreateProbability,
				), typeConverter, objectConverter, kind.GroupVersion()),
		), subresource,
	)
}

// DecodeManagedFields converts ManagedFields from the wire format (api format)
// to the format used by sigs.k8s.io/structured-merge-diff
func DecodeManagedFields(encodedManagedFields []metav1.ManagedFieldsEntry) (Managed, error) {
	return internal.DecodeManagedFields(encodedManagedFields)
}

func decodeLiveOrNew(liveObj, newObj runtime.Object, ignoreManagedFieldsFromRequestObject bool) (Managed, error) {
	liveAccessor, err := meta.Accessor(liveObj)
	if err != nil {
		return nil, err
	}

	// We take the managedFields of the live object in case the request tries to
	// manually set managedFields via a subresource.
	if ignoreManagedFieldsFromRequestObject {
		return emptyManagedFieldsOnErr(DecodeManagedFields(liveAccessor.GetManagedFields()))
	}

	// If the object doesn't have metadata, we should just return without trying to
	// set the managedFields at all, so creates/updates/patches will work normally.
	newAccessor, err := meta.Accessor(newObj)
	if err != nil {
		return nil, err
	}

	if isResetManagedFields(newAccessor.GetManagedFields()) {
		return internal.NewEmptyManaged(), nil
	}

	// If the managed field is empty or we failed to decode it,
	// let's try the live object. This is to prevent clients who
	// don't understand managedFields from deleting it accidentally.
	managed, err := DecodeManagedFields(newAccessor.GetManagedFields())
	if err != nil || len(managed.Fields()) == 0 {
		return emptyManagedFieldsOnErr(DecodeManagedFields(liveAccessor.GetManagedFields()))
	}
	return managed, nil
}

func emptyManagedFieldsOnErr(managed Managed, err error) (Managed, error) {
	if err != nil {
		return internal.NewEmptyManaged(), nil
	}
	return managed, nil
}

// Update is used when the object has already been merged (non-apply
// use-case), and simply updates the managed fields in the output
// object.
func (f *FieldManager) Update(liveObj, newObj runtime.Object, manager string) (object runtime.Object, err error) {
	// First try to decode the managed fields provided in the update,
	// This is necessary to allow directly updating managed fields.
	isSubresource := f.subresource != ""
	managed, err := decodeLiveOrNew(liveObj, newObj, isSubresource)
	if err != nil {
		return newObj, nil
	}

	internal.RemoveObjectManagedFields(liveObj)
	internal.RemoveObjectManagedFields(newObj)

	if object, managed, err = f.fieldManager.Update(liveObj, newObj, managed, manager); err != nil {
		return nil, err
	}

	if err = internal.EncodeObjectManagedFields(object, managed); err != nil {
		return nil, fmt.Errorf("failed to encode managed fields: %v", err)
	}

	return object, nil
}

// UpdateNoErrors is the same as Update, but it will not return
// errors. If an error happens, the object is returned with
// managedFields cleared.
func (f *FieldManager) UpdateNoErrors(liveObj, newObj runtime.Object, manager string) runtime.Object {
	obj, err := f.Update(liveObj, newObj, manager)
	if err != nil {
		atMostEverySecond.Do(func() {
			ns, name := "unknown", "unknown"
			accessor, err := meta.Accessor(newObj)
			if err == nil {
				ns = accessor.GetNamespace()
				name = accessor.GetName()
			}

			klog.ErrorS(err, "[SHOULD NOT HAPPEN] failed to update managedFields", "VersionKind",
				newObj.GetObjectKind().GroupVersionKind(), "namespace", ns, "name", name)
		})
		// Explicitly remove managedFields on failure, so that
		// we can't have garbage in it.
		internal.RemoveObjectManagedFields(newObj)
		return newObj
	}
	return obj
}

// Returns true if the managedFields indicate that the user is trying to
// reset the managedFields, i.e. if the list is non-nil but empty, or if
// the list has one empty item.
func isResetManagedFields(managedFields []metav1.ManagedFieldsEntry) bool {
	if len(managedFields) == 0 {
		return managedFields != nil
	}

	if len(managedFields) == 1 {
		return reflect.DeepEqual(managedFields[0], metav1.ManagedFieldsEntry{})
	}

	return false
}

// Apply is used when server-side apply is called, as it merges the
// object and updates the managed fields.
func (f *FieldManager) Apply(liveObj, appliedObj runtime.Object, manager string, force bool) (object runtime.Object, err error) {
	// If the object doesn't have metadata, apply isn't allowed.
	accessor, err := meta.Accessor(liveObj)
	if err != nil {
		return nil, fmt.Errorf("couldn't get accessor: %v", err)
	}

	// Decode the managed fields in the live object, since it isn't allowed in the patch.
	managed, err := DecodeManagedFields(accessor.GetManagedFields())
	if err != nil {
		return nil, fmt.Errorf("failed to decode managed fields: %v", err)
	}

	internal.RemoveObjectManagedFields(liveObj)

	object, managed, err = f.fieldManager.Apply(liveObj, appliedObj, managed, manager, force)
	if err != nil {
		if conflicts, ok := err.(merge.Conflicts); ok {
			return nil, internal.NewConflictError(conflicts)
		}
		return nil, err
	}

	if err = internal.EncodeObjectManagedFields(object, managed); err != nil {
		return nil, fmt.Errorf("failed to encode managed fields: %v", err)
	}

	return object, nil
}
<|endoftext|>


<commit_before>/*
Copyright 2017 The Rook Authors. All rights reserved.

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

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

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

// Package agent to manage Kubernetes storage attach events.
package agent

import (
	"encoding/json"
	"fmt"
	"os"

	"github.com/coreos/pkg/capnslog"
	rookalpha "github.com/rook/rook/pkg/apis/rook.io/v1alpha1"
	"github.com/rook/rook/pkg/daemon/agent/flexvolume/attachment"
	opcluster "github.com/rook/rook/pkg/operator/cluster"
	"github.com/rook/rook/pkg/operator/k8sutil"
	"k8s.io/api/core/v1"
	extensions "k8s.io/api/extensions/v1beta1"
	"k8s.io/api/rbac/v1beta1"
	kserrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
)

const (
	agentDaemonsetName             = "rook-agent"
	flexvolumePathDirEnv           = "FLEXVOLUME_DIR_PATH"
	flexvolumeDefaultDirPath       = "/usr/libexec/kubernetes/kubelet-plugins/volume/exec/"
	agentDaemonsetTolerationEnv    = "AGENT_TOLERATION"
	agentDaemonsetTolerationKeyEnv = "AGENT_TOLERATION_KEY"
)

var logger = capnslog.NewPackageLogger("github.com/rook/rook", "op-agent")

var clusterAccessRules = []v1beta1.PolicyRule{
	{
		APIGroups: []string{""},
		Resources: []string{"pods", "secrets", "configmaps", "persistentvolumes", "nodes", "nodes/proxy"},
		Verbs:     []string{"get", "list"},
	},
	{
		APIGroups: []string{rookalpha.CustomResourceGroup},
		Resources: []string{attachment.CustomResourceNamePlural},
		Verbs:     []string{"get", "list", "create", "delete", "update"},
	},
	{
		APIGroups: []string{rookalpha.CustomResourceGroup},
		Resources: []string{opcluster.CustomResourceNamePlural},
		Verbs:     []string{"get", "list", "watch"},
	},
	{
		APIGroups: []string{"storage.k8s.io"},
		Resources: []string{"storageclasses"},
		Verbs:     []string{"get"},
	},
}

// New creates an instance of Agent
func New(clientset kubernetes.Interface) *Agent {
	return &Agent{
		clientset: clientset,
	}
}

// Start the agent
func (a *Agent) Start(namespace, agentImage string) error {

	err := k8sutil.MakeClusterRole(a.clientset, namespace, agentDaemonsetName, clusterAccessRules, nil)
	if err != nil {
		return fmt.Errorf("failed to init RBAC for rook-agents. %+v", err)
	}

	err = a.createAgentDaemonSet(namespace, agentImage)
	if err != nil {
		return fmt.Errorf("Error starting agent daemonset: %v", err)
	}
	return nil
}

func (a *Agent) createAgentDaemonSet(namespace, agentImage string) error {

	flexvolumeDirPath, source := a.discoverFlexvolumeDir()
	logger.Infof("discovered flexvolume dir path from source %s. value: %s", source, flexvolumeDirPath)

	privileged := true
	ds := &extensions.DaemonSet{
		ObjectMeta: metav1.ObjectMeta{
			Name: agentDaemonsetName,
		},
		Spec: extensions.DaemonSetSpec{
			UpdateStrategy: extensions.DaemonSetUpdateStrategy{
				Type: extensions.RollingUpdateDaemonSetStrategyType,
			},
			Template: v1.PodTemplateSpec{
				ObjectMeta: metav1.ObjectMeta{
					Labels: map[string]string{
						"app": agentDaemonsetName,
					},
				},
				Spec: v1.PodSpec{
					ServiceAccountName: agentDaemonsetName,
					Containers: []v1.Container{
						{
							Name:  agentDaemonsetName,
							Image: agentImage,
							Args:  []string{"agent"},
							SecurityContext: &v1.SecurityContext{
								Privileged: &privileged,
							},
							VolumeMounts: []v1.VolumeMount{
								{
									Name:      "flexvolume",
									MountPath: "/flexmnt",
								},
								{
									Name:      "dev",
									MountPath: "/dev",
								},
								{
									Name:      "sys",
									MountPath: "/sys",
								},
								{
									Name:      "libmodules",
									MountPath: "/lib/modules",
								},
							},
							Env: []v1.EnvVar{
								k8sutil.NamespaceEnvVar(),
								k8sutil.NodeEnvVar(),
							},
						},
					},
					Volumes: []v1.Volume{
						{
							Name: "flexvolume",
							VolumeSource: v1.VolumeSource{
								HostPath: &v1.HostPathVolumeSource{
									Path: flexvolumeDirPath,
								},
							},
						},
						{
							Name: "dev",
							VolumeSource: v1.VolumeSource{
								HostPath: &v1.HostPathVolumeSource{
									Path: "/dev",
								},
							},
						},
						{
							Name: "sys",
							VolumeSource: v1.VolumeSource{
								HostPath: &v1.HostPathVolumeSource{
									Path: "/sys",
								},
							},
						},
						{
							Name: "libmodules",
							VolumeSource: v1.VolumeSource{
								HostPath: &v1.HostPathVolumeSource{
									Path: "/lib/modules",
								},
							},
						},
					},
					HostNetwork: true,
				},
			},
		},
	}

	// Add toleration if any
	tolerationValue := os.Getenv(agentDaemonsetTolerationEnv)
	if tolerationValue != "" {
		ds.Spec.Template.Spec.Tolerations = []v1.Toleration{
			{
				Effect:   v1.TaintEffect(tolerationValue),
				Operator: v1.TolerationOpExists,
				Key:      os.Getenv(agentDaemonsetTolerationKeyEnv),
			},
		}
	}

	_, err := a.clientset.Extensions().DaemonSets(namespace).Create(ds)
	if err != nil {
		if !kserrors.IsAlreadyExists(err) {
			return fmt.Errorf("failed to create rook-agent daemon set. %+v", err)
		}
		logger.Infof("rook-agent daemonset already exists")
	} else {
		logger.Infof("rook-agent daemonset started")
	}
	return nil

}

func (a *Agent) discoverFlexvolumeDir() (flexvolumeDirPath, source string) {
	//copy flexvolume to flexvolume dir
	nodeName := os.Getenv(k8sutil.NodeNameEnvVar)
	if nodeName == "" {
		logger.Warningf("cannot detect the node name. Please provide using the downward API in the rook operator manifest file")
		return getDefaultFlexvolumeDir()
	}

	// determining where the path of the flexvolume dir on the node
	nodeConfigURI, err := k8sutil.NodeConfigURI()
	if err != nil {
		logger.Warningf(err.Error())
		return getDefaultFlexvolumeDir()
	}
	nodeConfig, err := a.clientset.Core().RESTClient().Get().RequestURI(nodeConfigURI).DoRaw()
	if err != nil {
		logger.Warningf("unable to query node configuration: %v", err)
		return getDefaultFlexvolumeDir()
	}

	// unmarshal to a NodeConfigKubelet
	configKubelet := NodeConfigKubelet{}
	if err := json.Unmarshal(nodeConfig, &configKubelet); err != nil {
		logger.Warningf("unable to parse node config from Kubelet: %+v", err)
	} else {
		flexvolumeDirPath = configKubelet.ComponentConfig.VolumePluginDir
	}

	if flexvolumeDirPath != "" {
		return flexvolumeDirPath, "NodeConfigKubelet"
	}

	// unmarshal to a NodeConfigControllerManager
	configControllerManager := NodeConfigControllerManager{}
	if err := json.Unmarshal(nodeConfig, &configControllerManager); err != nil {
		logger.Warningf("unable to parse node config from controller manager: %+v", err)
	} else {
		flexvolumeDirPath = configControllerManager.ComponentConfig.VolumeConfiguration.FlexVolumePluginDir
	}

	if flexvolumeDirPath != "" {
		return flexvolumeDirPath, "NodeConfigControllerManager"
	}

	// unmarshal to a KubeletConfiguration
	kubeletConfiguration := KubeletConfiguration{}
	if err := json.Unmarshal(nodeConfig, &kubeletConfiguration); err != nil {
		logger.Warningf("unable to parse node config as kubelet configuration: %+v", err)
	} else {
		flexvolumeDirPath = kubeletConfiguration.KubeletConfig.VolumePluginDir
	}

	if flexvolumeDirPath != "" {
		return flexvolumeDirPath, "KubeletConfiguration"
	}

	return getDefaultFlexvolumeDir()
}

func getDefaultFlexvolumeDir() (flexvolumeDirPath, source string) {
	logger.Infof("getting flexvolume dir path from %s env var", flexvolumePathDirEnv)
	flexvolumeDirPath = os.Getenv(flexvolumePathDirEnv)
	if flexvolumeDirPath != "" {
		return flexvolumeDirPath, "env var"
	}

	logger.Infof("flexvolume dir path env var %s is not provided. Defaulting to: %s",
		flexvolumePathDirEnv, flexvolumeDefaultDirPath)
	flexvolumeDirPath = flexvolumeDefaultDirPath

	return flexvolumeDirPath, "default"
}
<commit_msg>operator: Update rook-agent DaemonSet if it already exists<commit_after>/*
Copyright 2017 The Rook Authors. All rights reserved.

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

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

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

// Package agent to manage Kubernetes storage attach events.
package agent

import (
	"encoding/json"
	"fmt"
	"os"

	"github.com/coreos/pkg/capnslog"
	rookalpha "github.com/rook/rook/pkg/apis/rook.io/v1alpha1"
	"github.com/rook/rook/pkg/daemon/agent/flexvolume/attachment"
	opcluster "github.com/rook/rook/pkg/operator/cluster"
	"github.com/rook/rook/pkg/operator/k8sutil"
	"k8s.io/api/core/v1"
	extensions "k8s.io/api/extensions/v1beta1"
	"k8s.io/api/rbac/v1beta1"
	kserrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
)

const (
	agentDaemonsetName             = "rook-agent"
	flexvolumePathDirEnv           = "FLEXVOLUME_DIR_PATH"
	flexvolumeDefaultDirPath       = "/usr/libexec/kubernetes/kubelet-plugins/volume/exec/"
	agentDaemonsetTolerationEnv    = "AGENT_TOLERATION"
	agentDaemonsetTolerationKeyEnv = "AGENT_TOLERATION_KEY"
)

var logger = capnslog.NewPackageLogger("github.com/rook/rook", "op-agent")

var clusterAccessRules = []v1beta1.PolicyRule{
	{
		APIGroups: []string{""},
		Resources: []string{"pods", "secrets", "configmaps", "persistentvolumes", "nodes", "nodes/proxy"},
		Verbs:     []string{"get", "list"},
	},
	{
		APIGroups: []string{rookalpha.CustomResourceGroup},
		Resources: []string{attachment.CustomResourceNamePlural},
		Verbs:     []string{"get", "list", "create", "delete", "update"},
	},
	{
		APIGroups: []string{rookalpha.CustomResourceGroup},
		Resources: []string{opcluster.CustomResourceNamePlural},
		Verbs:     []string{"get", "list", "watch"},
	},
	{
		APIGroups: []string{"storage.k8s.io"},
		Resources: []string{"storageclasses"},
		Verbs:     []string{"get"},
	},
}

// New creates an instance of Agent
func New(clientset kubernetes.Interface) *Agent {
	return &Agent{
		clientset: clientset,
	}
}

// Start the agent
func (a *Agent) Start(namespace, agentImage string) error {

	err := k8sutil.MakeClusterRole(a.clientset, namespace, agentDaemonsetName, clusterAccessRules, nil)
	if err != nil {
		return fmt.Errorf("failed to init RBAC for rook-agents. %+v", err)
	}

	err = a.createAgentDaemonSet(namespace, agentImage)
	if err != nil {
		return fmt.Errorf("Error starting agent daemonset: %v", err)
	}
	return nil
}

func (a *Agent) createAgentDaemonSet(namespace, agentImage string) error {

	flexvolumeDirPath, source := a.discoverFlexvolumeDir()
	logger.Infof("discovered flexvolume dir path from source %s. value: %s", source, flexvolumeDirPath)

	privileged := true
	ds := &extensions.DaemonSet{
		ObjectMeta: metav1.ObjectMeta{
			Name: agentDaemonsetName,
		},
		Spec: extensions.DaemonSetSpec{
			UpdateStrategy: extensions.DaemonSetUpdateStrategy{
				Type: extensions.RollingUpdateDaemonSetStrategyType,
			},
			Template: v1.PodTemplateSpec{
				ObjectMeta: metav1.ObjectMeta{
					Labels: map[string]string{
						"app": agentDaemonsetName,
					},
				},
				Spec: v1.PodSpec{
					ServiceAccountName: agentDaemonsetName,
					Containers: []v1.Container{
						{
							Name:  agentDaemonsetName,
							Image: agentImage,
							Args:  []string{"agent"},
							SecurityContext: &v1.SecurityContext{
								Privileged: &privileged,
							},
							VolumeMounts: []v1.VolumeMount{
								{
									Name:      "flexvolume",
									MountPath: "/flexmnt",
								},
								{
									Name:      "dev",
									MountPath: "/dev",
								},
								{
									Name:      "sys",
									MountPath: "/sys",
								},
								{
									Name:      "libmodules",
									MountPath: "/lib/modules",
								},
							},
							Env: []v1.EnvVar{
								k8sutil.NamespaceEnvVar(),
								k8sutil.NodeEnvVar(),
							},
						},
					},
					Volumes: []v1.Volume{
						{
							Name: "flexvolume",
							VolumeSource: v1.VolumeSource{
								HostPath: &v1.HostPathVolumeSource{
									Path: flexvolumeDirPath,
								},
							},
						},
						{
							Name: "dev",
							VolumeSource: v1.VolumeSource{
								HostPath: &v1.HostPathVolumeSource{
									Path: "/dev",
								},
							},
						},
						{
							Name: "sys",
							VolumeSource: v1.VolumeSource{
								HostPath: &v1.HostPathVolumeSource{
									Path: "/sys",
								},
							},
						},
						{
							Name: "libmodules",
							VolumeSource: v1.VolumeSource{
								HostPath: &v1.HostPathVolumeSource{
									Path: "/lib/modules",
								},
							},
						},
					},
					HostNetwork: true,
				},
			},
		},
	}

	// Add toleration if any
	tolerationValue := os.Getenv(agentDaemonsetTolerationEnv)
	if tolerationValue != "" {
		ds.Spec.Template.Spec.Tolerations = []v1.Toleration{
			{
				Effect:   v1.TaintEffect(tolerationValue),
				Operator: v1.TolerationOpExists,
				Key:      os.Getenv(agentDaemonsetTolerationKeyEnv),
			},
		}
	}

	_, err := a.clientset.Extensions().DaemonSets(namespace).Create(ds)
	if err != nil {
		if !kserrors.IsAlreadyExists(err) {
			return fmt.Errorf("failed to create rook-agent daemon set. %+v", err)
		}
		logger.Infof("rook-agent daemonset already exists, updating ...")
		_, err = a.clientset.Extensions().DaemonSets(namespace).Update(ds)
		if err != nil {
			return fmt.Errorf("failed to update rook-agent daemon set. %+v", err)
		}
	} else {
		logger.Infof("rook-agent daemonset started")
	}
	return nil

}

func (a *Agent) discoverFlexvolumeDir() (flexvolumeDirPath, source string) {
	//copy flexvolume to flexvolume dir
	nodeName := os.Getenv(k8sutil.NodeNameEnvVar)
	if nodeName == "" {
		logger.Warningf("cannot detect the node name. Please provide using the downward API in the rook operator manifest file")
		return getDefaultFlexvolumeDir()
	}

	// determining where the path of the flexvolume dir on the node
	nodeConfigURI, err := k8sutil.NodeConfigURI()
	if err != nil {
		logger.Warningf(err.Error())
		return getDefaultFlexvolumeDir()
	}
	nodeConfig, err := a.clientset.Core().RESTClient().Get().RequestURI(nodeConfigURI).DoRaw()
	if err != nil {
		logger.Warningf("unable to query node configuration: %v", err)
		return getDefaultFlexvolumeDir()
	}

	// unmarshal to a NodeConfigKubelet
	configKubelet := NodeConfigKubelet{}
	if err := json.Unmarshal(nodeConfig, &configKubelet); err != nil {
		logger.Warningf("unable to parse node config from Kubelet: %+v", err)
	} else {
		flexvolumeDirPath = configKubelet.ComponentConfig.VolumePluginDir
	}

	if flexvolumeDirPath != "" {
		return flexvolumeDirPath, "NodeConfigKubelet"
	}

	// unmarshal to a NodeConfigControllerManager
	configControllerManager := NodeConfigControllerManager{}
	if err := json.Unmarshal(nodeConfig, &configControllerManager); err != nil {
		logger.Warningf("unable to parse node config from controller manager: %+v", err)
	} else {
		flexvolumeDirPath = configControllerManager.ComponentConfig.VolumeConfiguration.FlexVolumePluginDir
	}

	if flexvolumeDirPath != "" {
		return flexvolumeDirPath, "NodeConfigControllerManager"
	}

	// unmarshal to a KubeletConfiguration
	kubeletConfiguration := KubeletConfiguration{}
	if err := json.Unmarshal(nodeConfig, &kubeletConfiguration); err != nil {
		logger.Warningf("unable to parse node config as kubelet configuration: %+v", err)
	} else {
		flexvolumeDirPath = kubeletConfiguration.KubeletConfig.VolumePluginDir
	}

	if flexvolumeDirPath != "" {
		return flexvolumeDirPath, "KubeletConfiguration"
	}

	return getDefaultFlexvolumeDir()
}

func getDefaultFlexvolumeDir() (flexvolumeDirPath, source string) {
	logger.Infof("getting flexvolume dir path from %s env var", flexvolumePathDirEnv)
	flexvolumeDirPath = os.Getenv(flexvolumePathDirEnv)
	if flexvolumeDirPath != "" {
		return flexvolumeDirPath, "env var"
	}

	logger.Infof("flexvolume dir path env var %s is not provided. Defaulting to: %s",
		flexvolumePathDirEnv, flexvolumeDefaultDirPath)
	flexvolumeDirPath = flexvolumeDefaultDirPath

	return flexvolumeDirPath, "default"
}
<|endoftext|>


<commit_before>package main

import (
	. "fmt"
	"html/template"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"strings"
)

func main() {
	flatsite := &FlatSite{}
	flatsite.OutputDir = getConf("OUTPUT_DIR", "www")
	flatsite.InputDir  = getConf("INPUT_DIR",  "tmpl")

	// walk filesystem, load all templates
	flatsite.Templates = template.New("templates")
	flatsite.Templates.Funcs(funcs)
	filepath.Walk(flatsite.InputDir, func(name string, info os.FileInfo, err error) error {
		isHidden := []rune(path.Base(name))[0] == '.'
		if (info.IsDir()) {
			if isHidden {
				return filepath.SkipDir
			} else {
				return nil
			}
		} else {
			if isHidden {
				return nil
			} else {
				if content, err := ioutil.ReadFile(name); err == nil {
					name, _ = filepath.Rel(flatsite.InputDir, name)
					flatsite.Templates.New(name).Parse(string(content))
				} else {
					Fprintf(os.Stderr, "%s \"%s\": %s\n", "failed to read template file", name, err)
				}
			}
		}
		return nil
	})

	// generate and output templates
	Printf("Generating public templates:\n")
	for _, v := range flatsite.Templates.Templates() {
		pth := NewPath(v.Name())
		if pth.Chunks[0] != "output" { continue; }
		Fprintf(os.Stdout, "\t%s : %#v\n", v.Name(), v)
		page := NewMap()
		page.Set("path", Path{Chunks: pth.Chunks[1:]})
		if err := flatsite.generateFile(v, page); err != nil {
			Fprintf(os.Stderr, "\t\t%s \"%s\": %s\n", "failed to generate from template", v.Name(), err)
		}
	}
}

func getConf(key string, defalt string) string {
	env := os.Getenv(key)
	if env == "" {
		return defalt
	} else {
		return env
	}
}

type FlatSite struct {
	OutputDir  string
	InputDir   string
	Templates  *template.Template
}

func NewPath(pth string) Path {
	return Path{
		Chunks: strings.Split(pth, string(os.PathSeparator)),
	}
}

type Path struct {
	Chunks []string
}

func (pth Path) String() string {
	return strings.Join(pth.Chunks, "/")
}

func (pth Path) Paths() []Path {
	n := len(pth.Chunks)
	v := make([]Path, n)
	for i := 0; i < n; i++ {
		v[i] = Path{Chunks: pth.Chunks[0:i+1]}
	}
	return v
}

func (pth Path) LastChunk() string {
	return pth.Chunks[len(pth.Chunks)-1]
}

type Map map[string]interface{}

func NewMap() Map {
	m := make(map[string]interface{})
	m[""] = ""
	return m
}

func (m Map) Set(key string, value interface{}) string {
	if _, ok := m[""]; len(m) == 1 && ok { delete(m, ""); }
	m[key] = value
	return ""
}

func (m Map) Get(key string) interface{} {
	return m[key]
}

var funcs = template.FuncMap{
	"eq": func(a interface{}, b interface{}) bool {
		return a == b
	},
	"NewMap": func() Map {
		return NewMap()
	},
	"Set": func(m Map, key string, value interface{}) string {
		return m.Set(key, value)
	},
}

func (flatsite *FlatSite) generateFile(tmpl *template.Template, page Map) error {
	outputPath := filepath.Join(page.Get("path").(Path).Chunks...)
	outputPathFull := filepath.Join(flatsite.OutputDir, outputPath)
	os.MkdirAll(path.Dir(outputPathFull), 0755)
	w, err := os.Create(outputPathFull)
	if err != nil {
		return Errorf("error creating static file %s: %s", outputPath, err)
	}
	defer w.Close()

	return tmpl.Execute(w, &page)
}
<commit_msg>alter the top-level Set function to return the value set.<commit_after>package main

import (
	. "fmt"
	"html/template"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"strings"
)

func main() {
	flatsite := &FlatSite{}
	flatsite.OutputDir = getConf("OUTPUT_DIR", "www")
	flatsite.InputDir  = getConf("INPUT_DIR",  "tmpl")

	// walk filesystem, load all templates
	flatsite.Templates = template.New("templates")
	flatsite.Templates.Funcs(funcs)
	filepath.Walk(flatsite.InputDir, func(name string, info os.FileInfo, err error) error {
		isHidden := []rune(path.Base(name))[0] == '.'
		if (info.IsDir()) {
			if isHidden {
				return filepath.SkipDir
			} else {
				return nil
			}
		} else {
			if isHidden {
				return nil
			} else {
				if content, err := ioutil.ReadFile(name); err == nil {
					name, _ = filepath.Rel(flatsite.InputDir, name)
					flatsite.Templates.New(name).Parse(string(content))
				} else {
					Fprintf(os.Stderr, "%s \"%s\": %s\n", "failed to read template file", name, err)
				}
			}
		}
		return nil
	})

	// generate and output templates
	Printf("Generating public templates:\n")
	for _, v := range flatsite.Templates.Templates() {
		pth := NewPath(v.Name())
		if pth.Chunks[0] != "output" { continue; }
		Fprintf(os.Stdout, "\t%s : %#v\n", v.Name(), v)
		page := NewMap()
		page.Set("path", Path{Chunks: pth.Chunks[1:]})
		if err := flatsite.generateFile(v, page); err != nil {
			Fprintf(os.Stderr, "\t\t%s \"%s\": %s\n", "failed to generate from template", v.Name(), err)
		}
	}
}

func getConf(key string, defalt string) string {
	env := os.Getenv(key)
	if env == "" {
		return defalt
	} else {
		return env
	}
}

type FlatSite struct {
	OutputDir  string
	InputDir   string
	Templates  *template.Template
}

func NewPath(pth string) Path {
	return Path{
		Chunks: strings.Split(pth, string(os.PathSeparator)),
	}
}

type Path struct {
	Chunks []string
}

func (pth Path) String() string {
	return strings.Join(pth.Chunks, "/")
}

func (pth Path) Paths() []Path {
	n := len(pth.Chunks)
	v := make([]Path, n)
	for i := 0; i < n; i++ {
		v[i] = Path{Chunks: pth.Chunks[0:i+1]}
	}
	return v
}

func (pth Path) LastChunk() string {
	return pth.Chunks[len(pth.Chunks)-1]
}

type Map map[string]interface{}

func NewMap() Map {
	m := make(map[string]interface{})
	m[""] = ""
	return m
}

func (m Map) Set(key string, value interface{}) string {
	if _, ok := m[""]; len(m) == 1 && ok { delete(m, ""); }
	m[key] = value
	return ""
}

func (m Map) Get(key string) interface{} {
	return m[key]
}

var funcs = template.FuncMap{
	"eq": func(a interface{}, b interface{}) bool {
		return a == b
	},
	"NewMap": func() Map {
		return NewMap()
	},
	"Set": func(m Map, key string, value interface{}) interface{} {
		m.Set(key, value)
		return value
	},
}

func (flatsite *FlatSite) generateFile(tmpl *template.Template, page Map) error {
	outputPath := filepath.Join(page.Get("path").(Path).Chunks...)
	outputPathFull := filepath.Join(flatsite.OutputDir, outputPath)
	os.MkdirAll(path.Dir(outputPathFull), 0755)
	w, err := os.Create(outputPathFull)
	if err != nil {
		return Errorf("error creating static file %s: %s", outputPath, err)
	}
	defer w.Close()

	return tmpl.Execute(w, &page)
}
<|endoftext|>


<commit_before>package paypal_rest

import (
	"database/sql"
	"errors"
	"time"

	"github.com/fritzpay/paymentd/pkg/paymentd/payment"
)

var (
	ErrTransactionNotFound = errors.New("transaction not found")
)

const selectTransaction = `
SELECT
	t.project_id,
	t.payment_id,
	t.timestamp,
	t.type,
	t.nonce,
	t.intent,
	t.paypal_id,
	t.payer_id,
	t.paypal_create_time,
	t.paypal_state,
	t.paypal_update_time,
	t.links,
	t.data
`

const selectTransactionCurrentByPaymentID = selectTransaction + `
FROM provider_paypal_transaction AS t
WHERE
	t.project_id = ?
	AND
	t.payment_id = ?
	AND
	t.timestamp = (
		SELECT MAX(timestamp) FROM provider_paypal_transaction
		WHERE
			project_id = t.project_id
			AND
			payment_id = t.payment_id
	)
`
const selectTransactionByPaymentIDAndNonce = selectTransaction + `
FROM provider_paypal_transaction AS tn
INNER JOIN provider_paypal_transaction AS t ON
	t.project_id = tn.project_id
	AND
	t.payment_id = tn.payment_id
	AND
	t.timestamp = (
		SELECT MAX(timestamp) FROM provider_paypal_transaction
		WHERE
			project_id = t.project_id
			AND
			payment_id = t.payment_id
	)
WHERE
	tn.project_id = ?
	AND
	tn.payment_id = ?
	AND
	tn.nonce = ?
`

func scanTransactionRow(row *sql.Row) (*Transaction, error) {
	t := &Transaction{}
	var ts int64
	err := row.Scan(
		&t.ProjectID,
		&t.PaymentID,
		&ts,
		&t.Type,
		&t.Nonce,
		&t.Intent,
		&t.PaypalID,
		&t.PayerID,
		&t.PaypalCreateTime,
		&t.PaypalState,
		&t.PaypalUpdateTime,
		&t.Links,
		&t.Data,
	)
	if err != nil {
		if err == sql.ErrNoRows {
			return t, ErrTransactionNotFound
		}
		return t, err
	}
	t.Timestamp = time.Unix(0, ts)
	return t, nil
}

func TransactionCurrentByPaymentIDTx(db *sql.Tx, paymentID payment.PaymentID) (*Transaction, error) {
	row := db.QueryRow(selectTransactionCurrentByPaymentID, paymentID.ProjectID, paymentID.PaymentID)
	return scanTransactionRow(row)
}

func TransactionCurrentByPaymentIDDB(db *sql.DB, paymentID payment.PaymentID) (*Transaction, error) {
	row := db.QueryRow(selectTransactionCurrentByPaymentID, paymentID.ProjectID, paymentID.PaymentID)
	return scanTransactionRow(row)
}

func TransactionByPaymentIDAndNonceTx(db *sql.Tx, paymentID payment.PaymentID, nonce string) (*Transaction, error) {
	row := db.QueryRow(selectTransactionByPaymentIDAndNonce, paymentID.ProjectID, paymentID.PaymentID, nonce)
	return scanTransactionRow(row)
}

func TransactionByPaymentIDAndNonceDB(db *sql.DB, paymentID payment.PaymentID, nonce string) (*Transaction, error) {
	row := db.QueryRow(selectTransactionByPaymentIDAndNonce, paymentID.ProjectID, paymentID.PaymentID, nonce)
	return scanTransactionRow(row)
}

const insertTransaction = `
INSERT INTO provider_paypal_transaction
(project_id, payment_id, timestamp, type, nonce, intent, paypal_id, payer_id, paypal_create_time, paypal_state, paypal_update_time, links, data)
VALUES
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`

func doInsertTransaction(stmt *sql.Stmt, t *Transaction) error {
	_, err := stmt.Exec(
		t.ProjectID,
		t.PaymentID,
		t.Timestamp.UnixNano(),
		t.Type,
		t.Nonce,
		t.Intent,
		t.PaypalID,
		t.PayerID,
		t.PaypalCreateTime,
		t.PaypalState,
		t.PaypalUpdateTime,
		t.Links,
		t.Data,
	)
	stmt.Close()
	return err
}

func InsertTransactionTx(db *sql.Tx, t *Transaction) error {
	stmt, err := db.Prepare(insertTransaction)
	if err != nil {
		return err
	}
	return doInsertTransaction(stmt, t)
}

func InsertTransactionDB(db *sql.DB, t *Transaction) error {
	stmt, err := db.Prepare(insertTransaction)
	if err != nil {
		return err
	}
	return doInsertTransaction(stmt, t)
}
<commit_msg>select payment by type<commit_after>package paypal_rest

import (
	"database/sql"
	"errors"
	"time"

	"github.com/fritzpay/paymentd/pkg/paymentd/payment"
)

var (
	ErrTransactionNotFound = errors.New("transaction not found")
)

const selectTransaction = `
SELECT
	t.project_id,
	t.payment_id,
	t.timestamp,
	t.type,
	t.nonce,
	t.intent,
	t.paypal_id,
	t.payer_id,
	t.paypal_create_time,
	t.paypal_state,
	t.paypal_update_time,
	t.links,
	t.data
`

const selectTransactionCurrentByPaymentID = selectTransaction + `
FROM provider_paypal_transaction AS t
WHERE
	t.project_id = ?
	AND
	t.payment_id = ?
	AND
	t.timestamp = (
		SELECT MAX(timestamp) FROM provider_paypal_transaction
		WHERE
			project_id = t.project_id
			AND
			payment_id = t.payment_id
	)
`
const selectTransactionByPaymentIDAndNonce = selectTransaction + `
FROM provider_paypal_transaction AS tn
INNER JOIN provider_paypal_transaction AS t ON
	t.project_id = tn.project_id
	AND
	t.payment_id = tn.payment_id
	AND
	t.timestamp = (
		SELECT MAX(timestamp) FROM provider_paypal_transaction
		WHERE
			project_id = t.project_id
			AND
			payment_id = t.payment_id
	)
WHERE
	tn.project_id = ?
	AND
	tn.payment_id = ?
	AND
	tn.nonce = ?
`

const selectTransactionByPaymentIDAndType = selectTransaction + `
FROM provider_paypal_transaction AS t
WHERE
	t.project_id = ?
	AND
	t.payment_id = ?
	AND
	t.timestamp = (
		SELECT MAX(timestamp) FROM provider_paypal_transaction
		WHERE
			project_id = t.project_id
			AND
			payment_id = t.payment_id
			AND
			type = ?
	)
`

func scanTransactionRow(row *sql.Row) (*Transaction, error) {
	t := &Transaction{}
	var ts int64
	err := row.Scan(
		&t.ProjectID,
		&t.PaymentID,
		&ts,
		&t.Type,
		&t.Nonce,
		&t.Intent,
		&t.PaypalID,
		&t.PayerID,
		&t.PaypalCreateTime,
		&t.PaypalState,
		&t.PaypalUpdateTime,
		&t.Links,
		&t.Data,
	)
	if err != nil {
		if err == sql.ErrNoRows {
			return t, ErrTransactionNotFound
		}
		return t, err
	}
	t.Timestamp = time.Unix(0, ts)
	return t, nil
}

func TransactionCurrentByPaymentIDTx(db *sql.Tx, paymentID payment.PaymentID) (*Transaction, error) {
	row := db.QueryRow(selectTransactionCurrentByPaymentID, paymentID.ProjectID, paymentID.PaymentID)
	return scanTransactionRow(row)
}

func TransactionCurrentByPaymentIDDB(db *sql.DB, paymentID payment.PaymentID) (*Transaction, error) {
	row := db.QueryRow(selectTransactionCurrentByPaymentID, paymentID.ProjectID, paymentID.PaymentID)
	return scanTransactionRow(row)
}

func TransactionByPaymentIDAndNonceTx(db *sql.Tx, paymentID payment.PaymentID, nonce string) (*Transaction, error) {
	row := db.QueryRow(selectTransactionByPaymentIDAndNonce, paymentID.ProjectID, paymentID.PaymentID, nonce)
	return scanTransactionRow(row)
}

func TransactionByPaymentIDAndNonceDB(db *sql.DB, paymentID payment.PaymentID, nonce string) (*Transaction, error) {
	row := db.QueryRow(selectTransactionByPaymentIDAndNonce, paymentID.ProjectID, paymentID.PaymentID, nonce)
	return scanTransactionRow(row)
}

func TransactionByPaymentIDAndTypeDB(db *sql.DB, paymentID payment.PaymentID, t string) (*Transaction, error) {
	row := db.QueryRow(selectTransactionByPaymentIDAndType, paymentID.ProjectID, paymentID.PaymentID, t)
	return scanTransactionRow(row)
}

const insertTransaction = `
INSERT INTO provider_paypal_transaction
(project_id, payment_id, timestamp, type, nonce, intent, paypal_id, payer_id, paypal_create_time, paypal_state, paypal_update_time, links, data)
VALUES
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`

func doInsertTransaction(stmt *sql.Stmt, t *Transaction) error {
	_, err := stmt.Exec(
		t.ProjectID,
		t.PaymentID,
		t.Timestamp.UnixNano(),
		t.Type,
		t.Nonce,
		t.Intent,
		t.PaypalID,
		t.PayerID,
		t.PaypalCreateTime,
		t.PaypalState,
		t.PaypalUpdateTime,
		t.Links,
		t.Data,
	)
	stmt.Close()
	return err
}

func InsertTransactionTx(db *sql.Tx, t *Transaction) error {
	stmt, err := db.Prepare(insertTransaction)
	if err != nil {
		return err
	}
	return doInsertTransaction(stmt, t)
}

func InsertTransactionDB(db *sql.DB, t *Transaction) error {
	stmt, err := db.Prepare(insertTransaction)
	if err != nil {
		return err
	}
	return doInsertTransaction(stmt, t)
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package inode

import (
	"fmt"
	"io"
	"math"

	"github.com/googlecloudplatform/gcsfuse/gcsproxy"
	"github.com/googlecloudplatform/gcsfuse/lease"
	"github.com/googlecloudplatform/gcsfuse/timeutil"
	"github.com/jacobsa/fuse/fuseops"
	"github.com/jacobsa/gcloud/gcs"
	"github.com/jacobsa/gcloud/syncutil"
	"golang.org/x/net/context"
)

type FileInode struct {
	/////////////////////////
	// Dependencies
	/////////////////////////

	bucket gcs.Bucket

	/////////////////////////
	// Constant data
	/////////////////////////

	id           fuseops.InodeID
	supportNlink bool
	name         string

	/////////////////////////
	// Mutable state
	/////////////////////////

	// A mutex that must be held when calling certain methods. See documentation
	// for each method.
	mu syncutil.InvariantMutex

	// GUARDED_BY(mu)
	lc lookupCount

	// A proxy for the backing object in GCS.
	//
	// INVARIANT: proxy.CheckInvariants() does not panic
	//
	// GUARDED_BY(mu)
	proxy *gcsproxy.MutableObject

	// Has Destroy been called?
	//
	// GUARDED_BY(mu)
	destroyed bool
}

var _ Inode = &FileInode{}

// Create a file inode for the given object in GCS. The initial lookup count is
// zero.
//
// If supportNlink is set, Attributes will use bucket.StatObject to find out
// whether the backing objet has been clobbered. Otherwise, Attributes will
// always show Nlink == 1.
//
// REQUIRES: o != nil
// REQUIRES: o.Generation > 0
// REQUIRES: len(o.Name) > 0
// REQUIRES: o.Name[len(o.Name)-1] != '/'
func NewFileInode(
	id fuseops.InodeID,
	o *gcs.Object,
	supportNlink bool,
	bucket gcs.Bucket,
	leaser lease.FileLeaser,
	clock timeutil.Clock) (f *FileInode) {
	// TODO(jacobsa): Allow configuring this.
	const chunkSize = math.MaxUint64

	// Set up the basic struct.
	f = &FileInode{
		bucket:       bucket,
		id:           id,
		name:         o.Name,
		supportNlink: supportNlink,
		proxy:        gcsproxy.NewMutableObject(chunkSize, o, bucket, leaser, clock),
	}

	f.lc.Init(id)

	// Set up invariant checking.
	f.mu = syncutil.NewInvariantMutex(f.checkInvariants)

	return
}

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

// LOCKS_REQUIRED(f.mu)
func (f *FileInode) checkInvariants() {
	if f.destroyed {
		return
	}

	// Make sure the name is legal.
	name := f.Name()
	if len(name) == 0 || name[len(name)-1] == '/' {
		panic("Illegal file name: " + name)
	}

	// INVARIANT: proxy.CheckInvariants() does not panic
	f.proxy.CheckInvariants()
}

////////////////////////////////////////////////////////////////////////
// Public interface
////////////////////////////////////////////////////////////////////////

func (f *FileInode) Lock() {
	f.mu.Lock()
}

func (f *FileInode) Unlock() {
	f.mu.Unlock()
}

func (f *FileInode) ID() fuseops.InodeID {
	return f.id
}

func (f *FileInode) Name() string {
	return f.name
}

// Return the object generation number from which this inode was branched.
//
// Does not require the lock to be held, but may spontaneously increase if the
// lock is not held.
func (f *FileInode) SourceGeneration() int64 {
	return f.proxy.SourceGeneration()
}

// LOCKS_REQUIRED(f.mu)
func (f *FileInode) IncrementLookupCount() {
	f.lc.Inc()
}

// LOCKS_REQUIRED(f.mu)
func (f *FileInode) DecrementLookupCount(n uint64) (destroy bool) {
	destroy = f.lc.Dec(n)
	return
}

// LOCKS_REQUIRED(f.mu)
func (f *FileInode) Destroy() (err error) {
	f.destroyed = true

	err = f.proxy.Destroy()
	return
}

// LOCKS_REQUIRED(f.mu)
func (f *FileInode) Attributes(
	ctx context.Context) (attrs fuseops.InodeAttributes, err error) {
	// Stat the object.
	sr, err := f.proxy.Stat(ctx, f.supportNlink)
	if err != nil {
		err = fmt.Errorf("Stat: %v", err)
		return
	}

	// Fill out the struct.
	attrs = fuseops.InodeAttributes{
		Nlink: 1,
		Size:  uint64(sr.Size),
		Mode:  0700,
		Mtime: sr.Mtime,
	}

	// If the object has been clobbered, we reflect that as the inode being
	// unlinked.
	if sr.Clobbered {
		attrs.Nlink = 0
	}

	return
}

// Serve a read op for this file, without responding.
//
// LOCKS_REQUIRED(f.mu)
func (f *FileInode) Read(
	op *fuseops.ReadFileOp) (err error) {
	// Read from the proxy.
	buf := make([]byte, op.Size)
	n, err := f.proxy.ReadAt(op.Context(), buf, op.Offset)

	// We don't return errors for EOF. Otherwise, propagate errors.
	if err == io.EOF {
		err = nil
	} else if err != nil {
		err = fmt.Errorf("ReadAt: %v", err)
		return
	}

	// Fill in the response.
	op.Data = buf[:n]

	return
}

// Serve a write op for this file, without responding.
//
// LOCKS_REQUIRED(f.mu)
func (f *FileInode) Write(
	op *fuseops.WriteFileOp) (err error) {
	// Write to the proxy. Note that the proxy guarantees that it returns an
	// error for short writes.
	_, err = f.proxy.WriteAt(op.Context(), op.Data, op.Offset)

	return
}

// Write out contents to GCS. If this fails due to the generation having been
// clobbered, treat it as a non-error (simulating the inode having been
// unlinked).
//
// After this method succeeds, SourceGeneration will return the new generation
// by which this inode should be known (which may be the same as before). If it
// fails, the generation will not change.
//
// LOCKS_REQUIRED(f.mu)
func (f *FileInode) Sync(ctx context.Context) (err error) {
	// Write out the proxy's contents if it is dirty.
	err = f.proxy.Sync(ctx)

	// Special case: a precondition error means we were clobbered, which we treat
	// as being unlinked. There's no reason to return an error in that case.
	if _, ok := err.(*gcs.PreconditionError); ok {
		err = nil
	}

	// Propagate other errors.
	if err != nil {
		err = fmt.Errorf("ObjectProxy.Sync: %v", err)
		return
	}

	return
}

// Truncate the file to the specified size.
//
// LOCKS_REQUIRED(f.mu)
func (f *FileInode) Truncate(
	ctx context.Context,
	size int64) (err error) {
	err = f.proxy.Truncate(ctx, size)
	return
}
<commit_msg>Accept a chunk size in NewFileInode.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package inode

import (
	"fmt"
	"io"

	"github.com/googlecloudplatform/gcsfuse/gcsproxy"
	"github.com/googlecloudplatform/gcsfuse/lease"
	"github.com/googlecloudplatform/gcsfuse/timeutil"
	"github.com/jacobsa/fuse/fuseops"
	"github.com/jacobsa/gcloud/gcs"
	"github.com/jacobsa/gcloud/syncutil"
	"golang.org/x/net/context"
)

type FileInode struct {
	/////////////////////////
	// Dependencies
	/////////////////////////

	bucket gcs.Bucket

	/////////////////////////
	// Constant data
	/////////////////////////

	id           fuseops.InodeID
	supportNlink bool
	name         string

	/////////////////////////
	// Mutable state
	/////////////////////////

	// A mutex that must be held when calling certain methods. See documentation
	// for each method.
	mu syncutil.InvariantMutex

	// GUARDED_BY(mu)
	lc lookupCount

	// A proxy for the backing object in GCS.
	//
	// INVARIANT: proxy.CheckInvariants() does not panic
	//
	// GUARDED_BY(mu)
	proxy *gcsproxy.MutableObject

	// Has Destroy been called?
	//
	// GUARDED_BY(mu)
	destroyed bool
}

var _ Inode = &FileInode{}

// Create a file inode for the given object in GCS. The initial lookup count is
// zero.
//
// gcsChunkSize controls the maximum size of each individual read request made
// to GCS.
//
// If supportNlink is set, Attributes will use bucket.StatObject to find out
// whether the backing objet has been clobbered. Otherwise, Attributes will
// always show Nlink == 1.
//
// REQUIRES: o != nil
// REQUIRES: o.Generation > 0
// REQUIRES: len(o.Name) > 0
// REQUIRES: o.Name[len(o.Name)-1] != '/'
func NewFileInode(
	id fuseops.InodeID,
	o *gcs.Object,
	gcsChunkSize uint64,
	supportNlink bool,
	bucket gcs.Bucket,
	leaser lease.FileLeaser,
	clock timeutil.Clock) (f *FileInode) {
	// Set up the basic struct.
	f = &FileInode{
		bucket:       bucket,
		id:           id,
		name:         o.Name,
		supportNlink: supportNlink,
		proxy: gcsproxy.NewMutableObject(
			gcsChunkSize,
			o,
			bucket,
			leaser,
			clock),
	}

	f.lc.Init(id)

	// Set up invariant checking.
	f.mu = syncutil.NewInvariantMutex(f.checkInvariants)

	return
}

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

// LOCKS_REQUIRED(f.mu)
func (f *FileInode) checkInvariants() {
	if f.destroyed {
		return
	}

	// Make sure the name is legal.
	name := f.Name()
	if len(name) == 0 || name[len(name)-1] == '/' {
		panic("Illegal file name: " + name)
	}

	// INVARIANT: proxy.CheckInvariants() does not panic
	f.proxy.CheckInvariants()
}

////////////////////////////////////////////////////////////////////////
// Public interface
////////////////////////////////////////////////////////////////////////

func (f *FileInode) Lock() {
	f.mu.Lock()
}

func (f *FileInode) Unlock() {
	f.mu.Unlock()
}

func (f *FileInode) ID() fuseops.InodeID {
	return f.id
}

func (f *FileInode) Name() string {
	return f.name
}

// Return the object generation number from which this inode was branched.
//
// Does not require the lock to be held, but may spontaneously increase if the
// lock is not held.
func (f *FileInode) SourceGeneration() int64 {
	return f.proxy.SourceGeneration()
}

// LOCKS_REQUIRED(f.mu)
func (f *FileInode) IncrementLookupCount() {
	f.lc.Inc()
}

// LOCKS_REQUIRED(f.mu)
func (f *FileInode) DecrementLookupCount(n uint64) (destroy bool) {
	destroy = f.lc.Dec(n)
	return
}

// LOCKS_REQUIRED(f.mu)
func (f *FileInode) Destroy() (err error) {
	f.destroyed = true

	err = f.proxy.Destroy()
	return
}

// LOCKS_REQUIRED(f.mu)
func (f *FileInode) Attributes(
	ctx context.Context) (attrs fuseops.InodeAttributes, err error) {
	// Stat the object.
	sr, err := f.proxy.Stat(ctx, f.supportNlink)
	if err != nil {
		err = fmt.Errorf("Stat: %v", err)
		return
	}

	// Fill out the struct.
	attrs = fuseops.InodeAttributes{
		Nlink: 1,
		Size:  uint64(sr.Size),
		Mode:  0700,
		Mtime: sr.Mtime,
	}

	// If the object has been clobbered, we reflect that as the inode being
	// unlinked.
	if sr.Clobbered {
		attrs.Nlink = 0
	}

	return
}

// Serve a read op for this file, without responding.
//
// LOCKS_REQUIRED(f.mu)
func (f *FileInode) Read(
	op *fuseops.ReadFileOp) (err error) {
	// Read from the proxy.
	buf := make([]byte, op.Size)
	n, err := f.proxy.ReadAt(op.Context(), buf, op.Offset)

	// We don't return errors for EOF. Otherwise, propagate errors.
	if err == io.EOF {
		err = nil
	} else if err != nil {
		err = fmt.Errorf("ReadAt: %v", err)
		return
	}

	// Fill in the response.
	op.Data = buf[:n]

	return
}

// Serve a write op for this file, without responding.
//
// LOCKS_REQUIRED(f.mu)
func (f *FileInode) Write(
	op *fuseops.WriteFileOp) (err error) {
	// Write to the proxy. Note that the proxy guarantees that it returns an
	// error for short writes.
	_, err = f.proxy.WriteAt(op.Context(), op.Data, op.Offset)

	return
}

// Write out contents to GCS. If this fails due to the generation having been
// clobbered, treat it as a non-error (simulating the inode having been
// unlinked).
//
// After this method succeeds, SourceGeneration will return the new generation
// by which this inode should be known (which may be the same as before). If it
// fails, the generation will not change.
//
// LOCKS_REQUIRED(f.mu)
func (f *FileInode) Sync(ctx context.Context) (err error) {
	// Write out the proxy's contents if it is dirty.
	err = f.proxy.Sync(ctx)

	// Special case: a precondition error means we were clobbered, which we treat
	// as being unlinked. There's no reason to return an error in that case.
	if _, ok := err.(*gcs.PreconditionError); ok {
		err = nil
	}

	// Propagate other errors.
	if err != nil {
		err = fmt.Errorf("ObjectProxy.Sync: %v", err)
		return
	}

	return
}

// Truncate the file to the specified size.
//
// LOCKS_REQUIRED(f.mu)
func (f *FileInode) Truncate(
	ctx context.Context,
	size int64) (err error) {
	err = f.proxy.Truncate(ctx, size)
	return
}
<|endoftext|>


<commit_before>package graval

import (
	"crypto/tls"
	"errors"
	"fmt"
	"github.com/koofr/goevent"
	"math/rand"
	"net"
	"strconv"
	"strings"
	"time"
)

type PassivePorts struct {
	Low  int
	High int
}

type PassiveOpts struct {
	ListenAddress string
	NatAddress    string
	PassivePorts  *PassivePorts
}

// A data socket is used to send non-control data between the client and
// server.
type ftpDataSocket interface {
	Host() string

	Port() int

	// the standard io.Reader interface
	Read(p []byte) (n int, err error)

	// the standard io.Writer interface
	Write(p []byte) (n int, err error)

	// the standard io.Closer interface
	Close() error

	// wait for client to connect
	Wait(timeout time.Duration) bool
}

type ftpActiveSocket struct {
	conn      *net.TCPConn
	connected *goevent.Event
	host      string
	port      int
	logger    *ftpLogger
}

func newActiveSocket(host string, port int, logger *ftpLogger) (*ftpActiveSocket, error) {
	connectTo := buildTcpString(host, port)
	logger.Print("Opening active data connection to " + connectTo)
	raddr, err := net.ResolveTCPAddr("tcp", connectTo)
	if err != nil {
		logger.Print(err)
		return nil, err
	}
	tcpConn, err := net.DialTCP("tcp", nil, raddr)
	if err != nil {
		logger.Print(err)
		return nil, err
	}
	socket := new(ftpActiveSocket)
	socket.conn = tcpConn
	socket.host = host
	socket.port = port
	socket.logger = logger
	socket.connected = goevent.NewEvent()
	socket.connected.Set()
	return socket, nil
}

func (socket *ftpActiveSocket) Host() string {
	return socket.host
}

func (socket *ftpActiveSocket) Port() int {
	return socket.port
}

func (socket *ftpActiveSocket) Read(p []byte) (n int, err error) {
	return socket.conn.Read(p)
}

func (socket *ftpActiveSocket) Write(p []byte) (n int, err error) {
	return socket.conn.Write(p)
}

func (socket *ftpActiveSocket) Close() error {
	return socket.conn.Close()
}

func (socket *ftpActiveSocket) Wait(timeout time.Duration) bool {
	return socket.connected.WaitMax(timeout)
}

type ftpPassiveSocket struct {
	conn        net.Conn
	connected   *goevent.Event
	host        string
	port        int
	ingress     chan []byte
	egress      chan []byte
	logger      *ftpLogger
	passiveOpts *PassiveOpts
	tlsConfig   *tls.Config
}

func newPassiveSocket(logger *ftpLogger, passiveOpts *PassiveOpts, tlsConfig *tls.Config) (*ftpPassiveSocket, error) {
	socket := new(ftpPassiveSocket)
	socket.ingress = make(chan []byte)
	socket.egress = make(chan []byte)
	socket.logger = logger
	socket.passiveOpts = passiveOpts
	socket.tlsConfig = tlsConfig
	socket.connected = goevent.NewEvent()

	go socket.ListenAndServe()

	retries := 100

	for {
		if socket.Port() > 0 {
			break
		}

		retries -= 1

		if retries == 0 {
			return nil, fmt.Errorf("newPassiveSocket socket port not found")
		}

		time.Sleep(100 * time.Millisecond)
	}

	return socket, nil
}

func (socket *ftpPassiveSocket) Host() string {
	return socket.host
}

func (socket *ftpPassiveSocket) Port() int {
	return socket.port
}

func (socket *ftpPassiveSocket) Read(p []byte) (n int, err error) {
	if socket.waitForOpenSocket() == false {
		return 0, errors.New("data socket unavailable")
	}
	return socket.conn.Read(p)
}

func (socket *ftpPassiveSocket) Write(p []byte) (n int, err error) {
	if socket.waitForOpenSocket() == false {
		return 0, errors.New("data socket unavailable")
	}
	return socket.conn.Write(p)
}

func (socket *ftpPassiveSocket) Close() error {
	socket.logger.Print("closing passive data socket")

	if socket.conn == nil {
		return nil
	}

	return socket.conn.Close()
}

func (socket *ftpPassiveSocket) Wait(timeout time.Duration) bool {
	return socket.connected.WaitMax(timeout)
}

func (socket *ftpPassiveSocket) listenHost() string {
	if socket.passiveOpts.ListenAddress != "" {
		return socket.passiveOpts.ListenAddress
	} else {
		return "0.0.0.0"
	}
}

func (socket *ftpPassiveSocket) randomPort() int {
	if socket.passiveOpts.PassivePorts != nil {
		low := socket.passiveOpts.PassivePorts.Low
		high := socket.passiveOpts.PassivePorts.High

		return low + rand.Intn(high-low-1)
	} else {
		return 0
	}
}

func (socket *ftpPassiveSocket) ListenAndServe() {
	laddr, err := net.ResolveTCPAddr("tcp", socket.listenHost()+":0")

	if err != nil {
		socket.logger.Print(err)
		return
	}

	var listener *net.TCPListener

	retries := 100

	for {
		laddr.Port = socket.randomPort()

		listener, err = net.ListenTCP("tcp4", laddr)

		if err != nil {
			if retries > 0 {
				retries -= 1
				time.Sleep(10 * time.Millisecond)
				continue
			}

			socket.logger.Print(err)

			return
		}

		break
	}

	addr := listener.Addr()

	parts := strings.Split(addr.String(), ":")

	socket.host = parts[0]

	port, err := strconv.Atoi(parts[1])

	if err == nil {
		socket.port = port
	}

	tcpConn, err := listener.AcceptTCP()

	if err != nil {
		socket.logger.Print(err)
		return
	}

	if socket.tlsConfig != nil {
		socket.conn = tls.Server(tcpConn, socket.tlsConfig)
	} else {
		socket.conn = tcpConn
	}

	socket.connected.Set()
}

func (socket *ftpPassiveSocket) waitForOpenSocket() bool {
	retries := 0
	for {
		if socket.conn != nil {
			break
		}
		if retries > 3 {
			return false
		}
		socket.logger.Print("sleeping, socket isn't open")
		time.Sleep(500 * time.Millisecond)
		retries += 1
	}
	return true
}
<commit_msg>Close passive socket listener after connection is accepted<commit_after>package graval

import (
	"crypto/tls"
	"errors"
	"fmt"
	"github.com/koofr/goevent"
	"math/rand"
	"net"
	"strconv"
	"strings"
	"time"
)

type PassivePorts struct {
	Low  int
	High int
}

type PassiveOpts struct {
	ListenAddress string
	NatAddress    string
	PassivePorts  *PassivePorts
}

// A data socket is used to send non-control data between the client and
// server.
type ftpDataSocket interface {
	Host() string

	Port() int

	// the standard io.Reader interface
	Read(p []byte) (n int, err error)

	// the standard io.Writer interface
	Write(p []byte) (n int, err error)

	// the standard io.Closer interface
	Close() error

	// wait for client to connect
	Wait(timeout time.Duration) bool
}

type ftpActiveSocket struct {
	conn      *net.TCPConn
	connected *goevent.Event
	host      string
	port      int
	logger    *ftpLogger
}

func newActiveSocket(host string, port int, logger *ftpLogger) (*ftpActiveSocket, error) {
	connectTo := buildTcpString(host, port)
	logger.Print("Opening active data connection to " + connectTo)
	raddr, err := net.ResolveTCPAddr("tcp", connectTo)
	if err != nil {
		logger.Print(err)
		return nil, err
	}
	tcpConn, err := net.DialTCP("tcp", nil, raddr)
	if err != nil {
		logger.Print(err)
		return nil, err
	}
	socket := new(ftpActiveSocket)
	socket.conn = tcpConn
	socket.host = host
	socket.port = port
	socket.logger = logger
	socket.connected = goevent.NewEvent()
	socket.connected.Set()
	return socket, nil
}

func (socket *ftpActiveSocket) Host() string {
	return socket.host
}

func (socket *ftpActiveSocket) Port() int {
	return socket.port
}

func (socket *ftpActiveSocket) Read(p []byte) (n int, err error) {
	return socket.conn.Read(p)
}

func (socket *ftpActiveSocket) Write(p []byte) (n int, err error) {
	return socket.conn.Write(p)
}

func (socket *ftpActiveSocket) Close() error {
	return socket.conn.Close()
}

func (socket *ftpActiveSocket) Wait(timeout time.Duration) bool {
	return socket.connected.WaitMax(timeout)
}

type ftpPassiveSocket struct {
	conn        net.Conn
	connected   *goevent.Event
	host        string
	port        int
	ingress     chan []byte
	egress      chan []byte
	logger      *ftpLogger
	passiveOpts *PassiveOpts
	tlsConfig   *tls.Config
}

func newPassiveSocket(logger *ftpLogger, passiveOpts *PassiveOpts, tlsConfig *tls.Config) (*ftpPassiveSocket, error) {
	socket := new(ftpPassiveSocket)
	socket.ingress = make(chan []byte)
	socket.egress = make(chan []byte)
	socket.logger = logger
	socket.passiveOpts = passiveOpts
	socket.tlsConfig = tlsConfig
	socket.connected = goevent.NewEvent()

	go socket.ListenAndServe()

	retries := 100

	for {
		if socket.Port() > 0 {
			break
		}

		retries -= 1

		if retries == 0 {
			return nil, fmt.Errorf("newPassiveSocket socket port not found")
		}

		time.Sleep(100 * time.Millisecond)
	}

	return socket, nil
}

func (socket *ftpPassiveSocket) Host() string {
	return socket.host
}

func (socket *ftpPassiveSocket) Port() int {
	return socket.port
}

func (socket *ftpPassiveSocket) Read(p []byte) (n int, err error) {
	if socket.waitForOpenSocket() == false {
		return 0, errors.New("data socket unavailable")
	}
	return socket.conn.Read(p)
}

func (socket *ftpPassiveSocket) Write(p []byte) (n int, err error) {
	if socket.waitForOpenSocket() == false {
		return 0, errors.New("data socket unavailable")
	}
	return socket.conn.Write(p)
}

func (socket *ftpPassiveSocket) Close() error {
	socket.logger.Print("closing passive data socket")

	if socket.conn == nil {
		return nil
	}

	return socket.conn.Close()
}

func (socket *ftpPassiveSocket) Wait(timeout time.Duration) bool {
	return socket.connected.WaitMax(timeout)
}

func (socket *ftpPassiveSocket) listenHost() string {
	if socket.passiveOpts.ListenAddress != "" {
		return socket.passiveOpts.ListenAddress
	} else {
		return "0.0.0.0"
	}
}

func (socket *ftpPassiveSocket) randomPort() int {
	if socket.passiveOpts.PassivePorts != nil {
		low := socket.passiveOpts.PassivePorts.Low
		high := socket.passiveOpts.PassivePorts.High

		return low + rand.Intn(high-low-1)
	} else {
		return 0
	}
}

func (socket *ftpPassiveSocket) ListenAndServe() {
	laddr, err := net.ResolveTCPAddr("tcp", socket.listenHost()+":0")

	if err != nil {
		socket.logger.Print(err)
		return
	}

	var listener *net.TCPListener

	retries := 100

	for {
		laddr.Port = socket.randomPort()

		listener, err = net.ListenTCP("tcp4", laddr)

		if err != nil {
			if retries > 0 {
				retries -= 1
				time.Sleep(10 * time.Millisecond)
				continue
			}

			socket.logger.Print(err)

			return
		}

		break
	}

	defer listener.Close()

	addr := listener.Addr()

	parts := strings.Split(addr.String(), ":")

	socket.host = parts[0]

	port, err := strconv.Atoi(parts[1])

	if err == nil {
		socket.port = port
	}

	tcpConn, err := listener.AcceptTCP()

	if err != nil {
		socket.logger.Print(err)
		return
	}

	if socket.tlsConfig != nil {
		socket.conn = tls.Server(tcpConn, socket.tlsConfig)
	} else {
		socket.conn = tcpConn
	}

	socket.connected.Set()
}

func (socket *ftpPassiveSocket) waitForOpenSocket() bool {
	retries := 0
	for {
		if socket.conn != nil {
			break
		}
		if retries > 3 {
			return false
		}
		socket.logger.Print("sleeping, socket isn't open")
		time.Sleep(500 * time.Millisecond)
		retries += 1
	}
	return true
}
<|endoftext|>