<commit_before>package shell

import (
	"context"
	"flag"
	"fmt"
	"github.com/chrislusf/seaweedfs/weed/filer"
	"github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
	"github.com/chrislusf/seaweedfs/weed/pb/remote_pb"
	"github.com/chrislusf/seaweedfs/weed/util"
	"github.com/golang/protobuf/jsonpb"
	"github.com/golang/protobuf/proto"
	"io"
	"regexp"
	"strings"
)

func init() {
	Commands = append(Commands, &commandRemoteConfigure{})
}

type commandRemoteConfigure struct {
}

func (c *commandRemoteConfigure) Name() string {
	return "remote.configure"
}

func (c *commandRemoteConfigure) Help() string {
	return `remote storage configuration

	# see the current configurations
	remote.configure

	# set or update a configuration
	remote.configure -name=cloud1 -type=s3 -s3.access_key=xxx -s3.secret_key=yyy
	remote.configure -name=cloud2 -type=gcs -gcs.appCredentialsFile=~/service-account-file.json
	remote.configure -name=cloud3 -type=azure -azure.account_name=xxx -azure.account_key=yyy
	remote.configure -name=cloud4 -type=aliyun -aliyun.access_key=xxx -aliyun.secret_key=yyy -aliyun.endpoint=oss-cn-shenzhen.aliyuncs.com -aliyun.region=cn-sehnzhen
	remote.configure -name=cloud5 -type=tencent -tencent.secret_id=xxx -tencent.secret_key=yyy -tencent.endpoint=cos.ap-guangzhou.myqcloud.com
	remote.configure -name=cloud6 -type=wasabi -wasabi.access_key=xxx -wasabi.secret_key=yyy -wasabi.endpoint=s3.us-west-1.wasabisys.com -wasabi.region=us-west-1
	remote.configure -name=cloud7 -type=storj -storj.access_key=xxx -storj.secret_key=yyy -storj.endpoint=https://gateway.us1.storjshare.io
	remote.configure -name=cloud8 -type=filebase -filebase.access_key=xxx -filebase.secret_key=yyy -filebase.endpoint=https://s3.filebase.com

	# delete one configuration
	remote.configure -delete -name=cloud1

`
}

var (
	isAlpha = regexp.MustCompile(`^[A-Za-z][A-Za-z0-9]*$`).MatchString
)

func (c *commandRemoteConfigure) Do(args []string, commandEnv *CommandEnv, writer io.Writer) (err error) {

	conf := &remote_pb.RemoteConf{}

	remoteConfigureCommand := flag.NewFlagSet(c.Name(), flag.ContinueOnError)
	isDelete := remoteConfigureCommand.Bool("delete", false, "delete one remote storage by its name")

	remoteConfigureCommand.StringVar(&conf.Name, "name", "", "a short name to identify the remote storage")
	remoteConfigureCommand.StringVar(&conf.Type, "type", "s3", "[s3|gcs|azure|b2|aliyun|tencent|baidu|wasabi|hdfs|filebase|storj] storage type")

	remoteConfigureCommand.StringVar(&conf.S3AccessKey, "s3.access_key", "", "s3 access key")
	remoteConfigureCommand.StringVar(&conf.S3SecretKey, "s3.secret_key", "", "s3 secret key")
	remoteConfigureCommand.StringVar(&conf.S3Region, "s3.region", "us-east-2", "s3 region")
	remoteConfigureCommand.StringVar(&conf.S3Endpoint, "s3.endpoint", "", "endpoint for s3-compatible local object store")
	remoteConfigureCommand.StringVar(&conf.S3StorageClass, "s3.storage_class", "", "s3 storage class")
	remoteConfigureCommand.BoolVar(&conf.S3ForcePathStyle, "s3.force_path_style", true, "s3 force path style")
	remoteConfigureCommand.BoolVar(&conf.S3V4Signature, "s3.v4_signature", false, "s3 V4 signature")

	remoteConfigureCommand.StringVar(&conf.GcsGoogleApplicationCredentials, "gcs.appCredentialsFile", "", "google cloud storage credentials file, default to use env GOOGLE_APPLICATION_CREDENTIALS")

	remoteConfigureCommand.StringVar(&conf.AzureAccountName, "azure.account_name", "", "azure account name, default to use env AZURE_STORAGE_ACCOUNT")
	remoteConfigureCommand.StringVar(&conf.AzureAccountKey, "azure.account_key", "", "azure account name, default to use env AZURE_STORAGE_ACCESS_KEY")

	remoteConfigureCommand.StringVar(&conf.BackblazeKeyId, "b2.key_id", "", "backblaze keyID")
	remoteConfigureCommand.StringVar(&conf.BackblazeApplicationKey, "b2.application_key", "", "backblaze applicationKey. Note that your Master Application Key will not work with the S3 Compatible API. You must create a new key that is eligible for use. For more information: https://help.backblaze.com/hc/en-us/articles/360047425453")
	remoteConfigureCommand.StringVar(&conf.BackblazeEndpoint, "b2.endpoint", "", "backblaze endpoint")

	remoteConfigureCommand.StringVar(&conf.AliyunAccessKey, "aliyun.access_key", "", "Aliyun access key, default to use env ALICLOUD_ACCESS_KEY_ID")
	remoteConfigureCommand.StringVar(&conf.AliyunSecretKey, "aliyun.secret_key", "", "Aliyun secret key, default to use env ALICLOUD_ACCESS_KEY_SECRET")
	remoteConfigureCommand.StringVar(&conf.AliyunEndpoint, "aliyun.endpoint", "", "Aliyun endpoint")
	remoteConfigureCommand.StringVar(&conf.AliyunRegion, "aliyun.region", "", "Aliyun region")

	remoteConfigureCommand.StringVar(&conf.TencentSecretId, "tencent.secret_id", "", "Tencent Secret Id, default to use env COS_SECRETID")
	remoteConfigureCommand.StringVar(&conf.TencentSecretKey, "tencent.secret_key", "", "Tencent secret key, default to use env COS_SECRETKEY")
	remoteConfigureCommand.StringVar(&conf.TencentEndpoint, "tencent.endpoint", "", "Tencent endpoint")

	remoteConfigureCommand.StringVar(&conf.BaiduAccessKey, "baidu.access_key", "", "Baidu access key, default to use env BDCLOUD_ACCESS_KEY")
	remoteConfigureCommand.StringVar(&conf.BaiduSecretKey, "baidu.secret_key", "", "Baidu secret key, default to use env BDCLOUD_SECRET_KEY")
	remoteConfigureCommand.StringVar(&conf.BaiduEndpoint, "baidu.endpoint", "", "Baidu endpoint")
	remoteConfigureCommand.StringVar(&conf.BaiduRegion, "baidu.region", "", "Baidu region")

	remoteConfigureCommand.StringVar(&conf.WasabiAccessKey, "wasabi.access_key", "", "Wasabi access key")
	remoteConfigureCommand.StringVar(&conf.WasabiSecretKey, "wasabi.secret_key", "", "Wasabi secret key")
	remoteConfigureCommand.StringVar(&conf.WasabiEndpoint, "wasabi.endpoint", "", "Wasabi endpoint, see https://wasabi.com/wp-content/themes/wasabi/docs/API_Guide/index.html#t=topics%2Fapidiff-intro.htm")
	remoteConfigureCommand.StringVar(&conf.WasabiRegion, "wasabi.region", "", "Wasabi region")

	remoteConfigureCommand.StringVar(&conf.FilebaseAccessKey, "filebase.access_key", "", "Filebase access key")
	remoteConfigureCommand.StringVar(&conf.FilebaseSecretKey, "filebase.secret_key", "", "Filebase secret key")
	remoteConfigureCommand.StringVar(&conf.FilebaseEndpoint, "filebase.endpoint", "", "Filebase endpoint, https://s3.filebase.com")

	remoteConfigureCommand.StringVar(&conf.StorjAccessKey, "storj.access_key", "", "Storj access key")
	remoteConfigureCommand.StringVar(&conf.StorjSecretKey, "storj.secret_key", "", "Storj secret key")
	remoteConfigureCommand.StringVar(&conf.StorjEndpoint, "storj.endpoint", "", "Storj endpoint")

	var namenodes arrayFlags
	remoteConfigureCommand.Var(&namenodes, "hdfs.namenodes", "hdfs name node and port, example: namenode1:8020,namenode2:8020")
	remoteConfigureCommand.StringVar(&conf.HdfsUsername, "hdfs.username", "", "hdfs user name")
	remoteConfigureCommand.StringVar(&conf.HdfsServicePrincipalName, "hdfs.servicePrincipalName", "", `Kerberos service principal name for the namenode

Example: hdfs/namenode.hadoop.docker
Namenode running as service 'hdfs' with FQDN 'namenode.hadoop.docker'.
`)
	remoteConfigureCommand.StringVar(&conf.HdfsDataTransferProtection, "hdfs.dataTransferProtection", "", "[authentication|integrity|privacy] Kerberos data transfer protection")

	if err = remoteConfigureCommand.Parse(args); err != nil {
		return nil
	}

	if conf.Type != "s3" {
		// clear out the default values
		conf.S3Region = ""
		conf.S3ForcePathStyle = false
	}

	if conf.Name == "" {
		return c.listExistingRemoteStorages(commandEnv, writer)
	}

	if !isAlpha(conf.Name) {
		return fmt.Errorf("only letters and numbers allowed in name: %v", conf.Name)
	}

	if *isDelete {
		return c.deleteRemoteStorage(commandEnv, writer, conf.Name)
	}

	return c.saveRemoteStorage(commandEnv, writer, conf)

}

func (c *commandRemoteConfigure) listExistingRemoteStorages(commandEnv *CommandEnv, writer io.Writer) error {

	return filer_pb.ReadDirAllEntries(commandEnv, util.FullPath(filer.DirectoryEtcRemote), "", func(entry *filer_pb.Entry, isLast bool) error {
		if len(entry.Content) == 0 {
			fmt.Fprintf(writer, "skipping %s\n", entry.Name)
			return nil
		}
		if !strings.HasSuffix(entry.Name, filer.REMOTE_STORAGE_CONF_SUFFIX) {
			return nil
		}
		conf := &remote_pb.RemoteConf{}

		if err := proto.Unmarshal(entry.Content, conf); err != nil {
			return fmt.Errorf("unmarshal %s/%s: %v", filer.DirectoryEtcRemote, entry.Name, err)
		}

		conf.S3SecretKey = strings.Repeat("*", len(conf.S3SecretKey))

		m := jsonpb.Marshaler{
			EmitDefaults: false,
			Indent:       "  ",
		}

		err := m.Marshal(writer, conf)
		fmt.Fprintln(writer)

		return err
	})

}

func (c *commandRemoteConfigure) deleteRemoteStorage(commandEnv *CommandEnv, writer io.Writer, storageName string) error {

	return commandEnv.WithFilerClient(func(client filer_pb.SeaweedFilerClient) error {

		request := &filer_pb.DeleteEntryRequest{
			Directory:            filer.DirectoryEtcRemote,
			Name:                 storageName + filer.REMOTE_STORAGE_CONF_SUFFIX,
			IgnoreRecursiveError: false,
			IsDeleteData:         true,
			IsRecursive:          true,
			IsFromOtherCluster:   false,
			Signatures:           nil,
		}
		_, err := client.DeleteEntry(context.Background(), request)

		if err == nil {
			fmt.Fprintf(writer, "removed: %s\n", storageName)
		}

		return err

	})

}

func (c *commandRemoteConfigure) saveRemoteStorage(commandEnv *CommandEnv, writer io.Writer, conf *remote_pb.RemoteConf) error {

	data, err := proto.Marshal(conf)
	if err != nil {
		return err
	}

	if err = commandEnv.WithFilerClient(func(client filer_pb.SeaweedFilerClient) error {
		return filer.SaveInsideFiler(client, filer.DirectoryEtcRemote, conf.Name+filer.REMOTE_STORAGE_CONF_SUFFIX, data)
	}); err != nil && err != filer_pb.ErrNotFound {
		return err
	}

	return nil

}

type arrayFlags []string

func (i *arrayFlags) String() string {
	return "my string representation"
}

func (i *arrayFlags) Set(value string) error {
	*i = append(*i, value)
	return nil
}
<commit_msg>adjust example<commit_after>package shell

import (
	"context"
	"flag"
	"fmt"
	"github.com/chrislusf/seaweedfs/weed/filer"
	"github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
	"github.com/chrislusf/seaweedfs/weed/pb/remote_pb"
	"github.com/chrislusf/seaweedfs/weed/util"
	"github.com/golang/protobuf/jsonpb"
	"github.com/golang/protobuf/proto"
	"io"
	"regexp"
	"strings"
)

func init() {
	Commands = append(Commands, &commandRemoteConfigure{})
}

type commandRemoteConfigure struct {
}

func (c *commandRemoteConfigure) Name() string {
	return "remote.configure"
}

func (c *commandRemoteConfigure) Help() string {
	return `remote storage configuration

	# see the current configurations
	remote.configure

	# set or update a configuration
	remote.configure -name=cloud1 -type=s3 -s3.access_key=xxx -s3.secret_key=yyy -s3.region=us-east-2
	remote.configure -name=cloud2 -type=gcs -gcs.appCredentialsFile=~/service-account-file.json
	remote.configure -name=cloud3 -type=azure -azure.account_name=xxx -azure.account_key=yyy
	remote.configure -name=cloud4 -type=aliyun -aliyun.access_key=xxx -aliyun.secret_key=yyy -aliyun.endpoint=oss-cn-shenzhen.aliyuncs.com -aliyun.region=cn-sehnzhen
	remote.configure -name=cloud5 -type=tencent -tencent.secret_id=xxx -tencent.secret_key=yyy -tencent.endpoint=cos.ap-guangzhou.myqcloud.com
	remote.configure -name=cloud6 -type=wasabi -wasabi.access_key=xxx -wasabi.secret_key=yyy -wasabi.endpoint=s3.us-west-1.wasabisys.com -wasabi.region=us-west-1
	remote.configure -name=cloud7 -type=storj -storj.access_key=xxx -storj.secret_key=yyy -storj.endpoint=https://gateway.us1.storjshare.io
	remote.configure -name=cloud8 -type=filebase -filebase.access_key=xxx -filebase.secret_key=yyy -filebase.endpoint=https://s3.filebase.com

	# delete one configuration
	remote.configure -delete -name=cloud1

`
}

var (
	isAlpha = regexp.MustCompile(`^[A-Za-z][A-Za-z0-9]*$`).MatchString
)

func (c *commandRemoteConfigure) Do(args []string, commandEnv *CommandEnv, writer io.Writer) (err error) {

	conf := &remote_pb.RemoteConf{}

	remoteConfigureCommand := flag.NewFlagSet(c.Name(), flag.ContinueOnError)
	isDelete := remoteConfigureCommand.Bool("delete", false, "delete one remote storage by its name")

	remoteConfigureCommand.StringVar(&conf.Name, "name", "", "a short name to identify the remote storage")
	remoteConfigureCommand.StringVar(&conf.Type, "type", "s3", "[s3|gcs|azure|b2|aliyun|tencent|baidu|wasabi|hdfs|filebase|storj] storage type")

	remoteConfigureCommand.StringVar(&conf.S3AccessKey, "s3.access_key", "", "s3 access key")
	remoteConfigureCommand.StringVar(&conf.S3SecretKey, "s3.secret_key", "", "s3 secret key")
	remoteConfigureCommand.StringVar(&conf.S3Region, "s3.region", "us-east-2", "s3 region")
	remoteConfigureCommand.StringVar(&conf.S3Endpoint, "s3.endpoint", "", "endpoint for s3-compatible local object store")
	remoteConfigureCommand.StringVar(&conf.S3StorageClass, "s3.storage_class", "", "s3 storage class")
	remoteConfigureCommand.BoolVar(&conf.S3ForcePathStyle, "s3.force_path_style", true, "s3 force path style")
	remoteConfigureCommand.BoolVar(&conf.S3V4Signature, "s3.v4_signature", false, "s3 V4 signature")

	remoteConfigureCommand.StringVar(&conf.GcsGoogleApplicationCredentials, "gcs.appCredentialsFile", "", "google cloud storage credentials file, default to use env GOOGLE_APPLICATION_CREDENTIALS")

	remoteConfigureCommand.StringVar(&conf.AzureAccountName, "azure.account_name", "", "azure account name, default to use env AZURE_STORAGE_ACCOUNT")
	remoteConfigureCommand.StringVar(&conf.AzureAccountKey, "azure.account_key", "", "azure account name, default to use env AZURE_STORAGE_ACCESS_KEY")

	remoteConfigureCommand.StringVar(&conf.BackblazeKeyId, "b2.key_id", "", "backblaze keyID")
	remoteConfigureCommand.StringVar(&conf.BackblazeApplicationKey, "b2.application_key", "", "backblaze applicationKey. Note that your Master Application Key will not work with the S3 Compatible API. You must create a new key that is eligible for use. For more information: https://help.backblaze.com/hc/en-us/articles/360047425453")
	remoteConfigureCommand.StringVar(&conf.BackblazeEndpoint, "b2.endpoint", "", "backblaze endpoint")

	remoteConfigureCommand.StringVar(&conf.AliyunAccessKey, "aliyun.access_key", "", "Aliyun access key, default to use env ALICLOUD_ACCESS_KEY_ID")
	remoteConfigureCommand.StringVar(&conf.AliyunSecretKey, "aliyun.secret_key", "", "Aliyun secret key, default to use env ALICLOUD_ACCESS_KEY_SECRET")
	remoteConfigureCommand.StringVar(&conf.AliyunEndpoint, "aliyun.endpoint", "", "Aliyun endpoint")
	remoteConfigureCommand.StringVar(&conf.AliyunRegion, "aliyun.region", "", "Aliyun region")

	remoteConfigureCommand.StringVar(&conf.TencentSecretId, "tencent.secret_id", "", "Tencent Secret Id, default to use env COS_SECRETID")
	remoteConfigureCommand.StringVar(&conf.TencentSecretKey, "tencent.secret_key", "", "Tencent secret key, default to use env COS_SECRETKEY")
	remoteConfigureCommand.StringVar(&conf.TencentEndpoint, "tencent.endpoint", "", "Tencent endpoint")

	remoteConfigureCommand.StringVar(&conf.BaiduAccessKey, "baidu.access_key", "", "Baidu access key, default to use env BDCLOUD_ACCESS_KEY")
	remoteConfigureCommand.StringVar(&conf.BaiduSecretKey, "baidu.secret_key", "", "Baidu secret key, default to use env BDCLOUD_SECRET_KEY")
	remoteConfigureCommand.StringVar(&conf.BaiduEndpoint, "baidu.endpoint", "", "Baidu endpoint")
	remoteConfigureCommand.StringVar(&conf.BaiduRegion, "baidu.region", "", "Baidu region")

	remoteConfigureCommand.StringVar(&conf.WasabiAccessKey, "wasabi.access_key", "", "Wasabi access key")
	remoteConfigureCommand.StringVar(&conf.WasabiSecretKey, "wasabi.secret_key", "", "Wasabi secret key")
	remoteConfigureCommand.StringVar(&conf.WasabiEndpoint, "wasabi.endpoint", "", "Wasabi endpoint, see https://wasabi.com/wp-content/themes/wasabi/docs/API_Guide/index.html#t=topics%2Fapidiff-intro.htm")
	remoteConfigureCommand.StringVar(&conf.WasabiRegion, "wasabi.region", "", "Wasabi region")

	remoteConfigureCommand.StringVar(&conf.FilebaseAccessKey, "filebase.access_key", "", "Filebase access key")
	remoteConfigureCommand.StringVar(&conf.FilebaseSecretKey, "filebase.secret_key", "", "Filebase secret key")
	remoteConfigureCommand.StringVar(&conf.FilebaseEndpoint, "filebase.endpoint", "", "Filebase endpoint, https://s3.filebase.com")

	remoteConfigureCommand.StringVar(&conf.StorjAccessKey, "storj.access_key", "", "Storj access key")
	remoteConfigureCommand.StringVar(&conf.StorjSecretKey, "storj.secret_key", "", "Storj secret key")
	remoteConfigureCommand.StringVar(&conf.StorjEndpoint, "storj.endpoint", "", "Storj endpoint")

	var namenodes arrayFlags
	remoteConfigureCommand.Var(&namenodes, "hdfs.namenodes", "hdfs name node and port, example: namenode1:8020,namenode2:8020")
	remoteConfigureCommand.StringVar(&conf.HdfsUsername, "hdfs.username", "", "hdfs user name")
	remoteConfigureCommand.StringVar(&conf.HdfsServicePrincipalName, "hdfs.servicePrincipalName", "", `Kerberos service principal name for the namenode

Example: hdfs/namenode.hadoop.docker
Namenode running as service 'hdfs' with FQDN 'namenode.hadoop.docker'.
`)
	remoteConfigureCommand.StringVar(&conf.HdfsDataTransferProtection, "hdfs.dataTransferProtection", "", "[authentication|integrity|privacy] Kerberos data transfer protection")

	if err = remoteConfigureCommand.Parse(args); err != nil {
		return nil
	}

	if conf.Type != "s3" {
		// clear out the default values
		conf.S3Region = ""
		conf.S3ForcePathStyle = false
	}

	if conf.Name == "" {
		return c.listExistingRemoteStorages(commandEnv, writer)
	}

	if !isAlpha(conf.Name) {
		return fmt.Errorf("only letters and numbers allowed in name: %v", conf.Name)
	}

	if *isDelete {
		return c.deleteRemoteStorage(commandEnv, writer, conf.Name)
	}

	return c.saveRemoteStorage(commandEnv, writer, conf)

}

func (c *commandRemoteConfigure) listExistingRemoteStorages(commandEnv *CommandEnv, writer io.Writer) error {

	return filer_pb.ReadDirAllEntries(commandEnv, util.FullPath(filer.DirectoryEtcRemote), "", func(entry *filer_pb.Entry, isLast bool) error {
		if len(entry.Content) == 0 {
			fmt.Fprintf(writer, "skipping %s\n", entry.Name)
			return nil
		}
		if !strings.HasSuffix(entry.Name, filer.REMOTE_STORAGE_CONF_SUFFIX) {
			return nil
		}
		conf := &remote_pb.RemoteConf{}

		if err := proto.Unmarshal(entry.Content, conf); err != nil {
			return fmt.Errorf("unmarshal %s/%s: %v", filer.DirectoryEtcRemote, entry.Name, err)
		}

		conf.S3SecretKey = strings.Repeat("*", len(conf.S3SecretKey))

		m := jsonpb.Marshaler{
			EmitDefaults: false,
			Indent:       "  ",
		}

		err := m.Marshal(writer, conf)
		fmt.Fprintln(writer)

		return err
	})

}

func (c *commandRemoteConfigure) deleteRemoteStorage(commandEnv *CommandEnv, writer io.Writer, storageName string) error {

	return commandEnv.WithFilerClient(func(client filer_pb.SeaweedFilerClient) error {

		request := &filer_pb.DeleteEntryRequest{
			Directory:            filer.DirectoryEtcRemote,
			Name:                 storageName + filer.REMOTE_STORAGE_CONF_SUFFIX,
			IgnoreRecursiveError: false,
			IsDeleteData:         true,
			IsRecursive:          true,
			IsFromOtherCluster:   false,
			Signatures:           nil,
		}
		_, err := client.DeleteEntry(context.Background(), request)

		if err == nil {
			fmt.Fprintf(writer, "removed: %s\n", storageName)
		}

		return err

	})

}

func (c *commandRemoteConfigure) saveRemoteStorage(commandEnv *CommandEnv, writer io.Writer, conf *remote_pb.RemoteConf) error {

	data, err := proto.Marshal(conf)
	if err != nil {
		return err
	}

	if err = commandEnv.WithFilerClient(func(client filer_pb.SeaweedFilerClient) error {
		return filer.SaveInsideFiler(client, filer.DirectoryEtcRemote, conf.Name+filer.REMOTE_STORAGE_CONF_SUFFIX, data)
	}); err != nil && err != filer_pb.ErrNotFound {
		return err
	}

	return nil

}

type arrayFlags []string

func (i *arrayFlags) String() string {
	return "my string representation"
}

func (i *arrayFlags) Set(value string) error {
	*i = append(*i, value)
	return nil
}
<|endoftext|>


<commit_before>// +build appengine

package urlfetch

import (
	"io"
	"net/http"
	"net/url"
	"time"

	"appengine"
	"appengine/urlfetch"
	"github.com/blueoceans/go-common/errors"
	u "github.com/blueoceans/go-common/logutil"
)

const (
	reasonDeadlineExceededTimeout = "Deadline exceeded (timeout)"

	reasonUrlfetch = "urlfetch:"

	reasonUrlfetchDNSErr            = "urlfetch: DNS_ERROR"
	reasonUrlfetchInvalidURL        = "urlfetch: INVALID_URL"
	reasonUrlfetchSSLCertificateErr = "urlfetch: SSL_CERTIFICATE_ERROR"

	reasonUrlfetchClosed               = "urlfetch: CLOSED"
	reasonUrlfetchConnectionErr        = "urlfetch: CONNECTION_ERROR"
	reasonUrlfetchDeadlineExceeded     = "urlfetch: DEADLINE_EXCEEDED"
	reasonUrlfetchFetchErr             = "urlfetch: FETCH_ERROR"
	reasonUrlfetchInternalTransientErr = "urlfetch: INTERNAL_TRANSIENT_ERROR"

	sec1 = 1 * time.Second
)

var (
	errNoWait = []string{
		reasonDeadlineExceededTimeout,
		reasonUrlfetchClosed,
		reasonUrlfetchConnectionErr,
		reasonUrlfetchDeadlineExceeded,
		reasonUrlfetchFetchErr,
	}
	errWait = []string{
		reasonUrlfetchInternalTransientErr,
	}
)

// IsErrNoWait returns is whether it can re-try soon or not.
func IsErrNoWait(
	err error,
) bool {
	return errors.ContainsErrorMessage(err, errNoWait)
}

// IsErrWait returns is whether it must wait for next re-try or not.
func IsErrWait(
	err error,
) bool {
	return errors.ContainsErrorMessage(err, errWait)
}

func ClientGet(
	c appengine.Context,
	url string,
) (
	*http.Response,
	error,
) {
	return clientDo(
		c,
		func() (
			*http.Response,
			error,
		) {
			return urlfetch.Client(c).Get(url)
		},
	)
}

func ClientPost(
	c appengine.Context,
	url string,
	mimeType string,
	body io.Reader,
) (
	*http.Response,
	error,
) {
	return clientDo(
		c,
		func() (
			*http.Response,
			error,
		) {
			return urlfetch.Client(c).Post(url, mimeType, body)
		},
	)
}

func ClientPostForm(
	c appengine.Context,
	url string,
	data url.Values,
) (
	*http.Response,
	error,
) {
	return clientDo(
		c,
		func() (
			*http.Response,
			error,
		) {
			return urlfetch.Client(c).PostForm(url, data)
		},
	)
}

func clientDo(
	c appengine.Context,
	do func() (*http.Response, error),
) (
	*http.Response,
	error,
) {
retry:
	response, err := do()
	switch {
	case err == nil:
		return response, nil
	case IsErrNoWait(err):
		u.Infof(c, "%q", err)
		goto retry
	case IsErrWait(err):
		u.Infof(c, "%q", err)
		time.Sleep(sec1)
		goto retry
	default:
		return nil, err
	}
}
<commit_msg>add IsUrlfetchErr<commit_after>// +build appengine

package urlfetch

import (
	"io"
	"net/http"
	"net/url"
	"time"

	"appengine"
	"appengine/urlfetch"
	"github.com/blueoceans/go-common/errors"
	u "github.com/blueoceans/go-common/logutil"
)

const (
	reasonDeadlineExceededTimeout = "Deadline exceeded (timeout)"

	reasonUrlfetch = "urlfetch:"

	reasonUrlfetchDNSErr            = "urlfetch: DNS_ERROR"
	reasonUrlfetchInvalidURL        = "urlfetch: INVALID_URL"
	reasonUrlfetchSSLCertificateErr = "urlfetch: SSL_CERTIFICATE_ERROR"

	reasonUrlfetchClosed               = "urlfetch: CLOSED"
	reasonUrlfetchConnectionErr        = "urlfetch: CONNECTION_ERROR"
	reasonUrlfetchDeadlineExceeded     = "urlfetch: DEADLINE_EXCEEDED"
	reasonUrlfetchFetchErr             = "urlfetch: FETCH_ERROR"
	reasonUrlfetchInternalTransientErr = "urlfetch: INTERNAL_TRANSIENT_ERROR"

	sec1 = 1 * time.Second
)

var (
	errNoWait = []string{
		reasonDeadlineExceededTimeout,
		reasonUrlfetchClosed,
		reasonUrlfetchConnectionErr,
		reasonUrlfetchDeadlineExceeded,
		reasonUrlfetchFetchErr,
	}
	errWait = []string{
		reasonUrlfetchInternalTransientErr,
	}
)

// IsUrlfetchErr returns is whether it is `urlfetch` error or not.
func IsUrlfetchErr(
	err error,
) bool {
	return errors.ContainsErrorMessage(err, []string{reasonUrlfetch})
}

// IsErrNoWait returns is whether it can re-try soon or not.
func IsErrNoWait(
	err error,
) bool {
	return errors.ContainsErrorMessage(err, errNoWait)
}

// IsErrWait returns is whether it must wait for next re-try or not.
func IsErrWait(
	err error,
) bool {
	return errors.ContainsErrorMessage(err, errWait)
}

func ClientGet(
	c appengine.Context,
	url string,
) (
	*http.Response,
	error,
) {
	return clientDo(
		c,
		func() (
			*http.Response,
			error,
		) {
			return urlfetch.Client(c).Get(url)
		},
	)
}

func ClientPost(
	c appengine.Context,
	url string,
	mimeType string,
	body io.Reader,
) (
	*http.Response,
	error,
) {
	return clientDo(
		c,
		func() (
			*http.Response,
			error,
		) {
			return urlfetch.Client(c).Post(url, mimeType, body)
		},
	)
}

func ClientPostForm(
	c appengine.Context,
	url string,
	data url.Values,
) (
	*http.Response,
	error,
) {
	return clientDo(
		c,
		func() (
			*http.Response,
			error,
		) {
			return urlfetch.Client(c).PostForm(url, data)
		},
	)
}

func clientDo(
	c appengine.Context,
	do func() (*http.Response, error),
) (
	*http.Response,
	error,
) {
retry:
	response, err := do()
	switch {
	case err == nil:
		return response, nil
	case IsErrNoWait(err):
		u.Infof(c, "%q", err)
		goto retry
	case IsErrWait(err):
		u.Infof(c, "%q", err)
		time.Sleep(sec1)
		goto retry
	default:
		return nil, err
	}
}
<|endoftext|>


<commit_before>package fuse

// This file contains the internal logic of the
// FileSystemConnector. The functions for satisfying the raw interface are in
// fsops.go

import (
	"fmt"
	"log"
	"os"
	"path/filepath"
	"strings"
	"unsafe"
)

// Tests should set to true.
var paranoia = false

func NewFileSystemOptions() *FileSystemOptions {
	return &FileSystemOptions{
		NegativeTimeout: 0.0,
		AttrTimeout:     1.0,
		EntryTimeout:    1.0,
		Owner:           CurrentOwner(),
	}
}

// FilesystemConnector is a raw FUSE filesystem that manages in-process mounts and inodes.
type FileSystemConnector struct {
	DefaultRawFileSystem

	Debug bool

	fsInit   RawFsInit
	inodeMap HandleMap
	rootNode *Inode
}

func NewFileSystemConnector(nodeFs NodeFileSystem, opts *FileSystemOptions) (me *FileSystemConnector) {
	me = new(FileSystemConnector)
	if opts == nil {
		opts = NewFileSystemOptions()
	}
	me.inodeMap = NewHandleMap(!opts.SkipCheckHandles)
	me.rootNode = me.newInode(true)
	me.rootNode.nodeId = FUSE_ROOT_ID

	// FUSE does not issue a LOOKUP for 1 (obviously), but it does
	// issue a forget.  This lookupCount is to make the counts match.
	me.rootNode.addLookupCount(1)
	me.verify()
	me.MountRoot(nodeFs, opts)
	return me
}

func (me *FileSystemConnector) verify() {
	if !paranoia {
		return
	}
	root := me.rootNode
	root.verify(me.rootNode.mountPoint)
}

func (me *FileSystemConnector) newInode(isDir bool) *Inode {
	data := new(Inode)
	data.nodeId = me.inodeMap.Register(&data.handled, data)
	data.connector = me
	if isDir {
		data.children = make(map[string]*Inode, initDirSize)
	}

	return data
}

func (me *FileSystemConnector) createChild(parent *Inode, name string, fi *os.FileInfo, fsi FsNode) (out *EntryOut, child *Inode) {
	if fsi.Inode() == nil {
		child = parent.CreateChild(name, fi.IsDirectory(), fsi)
	} else {
		parent.treeLock.Lock()
		defer parent.treeLock.Unlock()
		child = fsi.Inode()
		child.addLookupCount(1)
	}

	out = parent.mount.fileInfoToEntry(fi)
	out.Ino = child.nodeId
	out.NodeId = child.nodeId
	return out, child
}

func (me *FileSystemConnector) lookupUpdate(parent *Inode, name string, isDir bool, lookupCount int) *Inode {
	defer me.verify()

	parent.treeLock.Lock()
	defer parent.treeLock.Unlock()

	data, ok := parent.children[name]
	if !ok {
		data = me.newInode(isDir)
		parent.addChild(name, data)
		data.mount = parent.mount
		data.treeLock = &data.mount.treeLock
	}
	data.addLookupCount(lookupCount)
	return data
}

func (me *FileSystemConnector) findMount(parent *Inode, name string) (mount *fileSystemMount) {
	parent.treeLock.RLock()
	defer parent.treeLock.RUnlock()
	if parent.mounts == nil {
		return nil
	}

	return parent.mounts[name]
}

func (me *FileSystemConnector) getInodeData(nodeid uint64) *Inode {
	if nodeid == FUSE_ROOT_ID {
		return me.rootNode
	}
	i := (*Inode)(unsafe.Pointer(DecodeHandle(nodeid)))
	return i
}

func (me *FileSystemConnector) forgetUpdate(nodeId uint64, forgetCount int) {
	defer me.verify()

	node := me.getInodeData(nodeId)

	node.treeLock.Lock()
	defer node.treeLock.Unlock()

	node.addLookupCount(-forgetCount)
	me.considerDropInode(node)
}

func (me *FileSystemConnector) considerDropInode(n *Inode) (drop bool) {
	delChildren := []string{}
	for k, v := range n.children {
		if v.mountPoint == nil && me.considerDropInode(v) {
			delChildren = append(delChildren, k)
		}
	}
	for _, k := range delChildren {
		ch := n.rmChild(k)
		if ch == nil {
			panic(fmt.Sprintf("trying to del child %q, but not present", k))
		}
		ch.fsInode.OnForget()
		me.inodeMap.Forget(ch.nodeId)
	}

	if len(n.children) > 0 || n.Live() {
		return false
	}
	if n == me.rootNode || n.mountPoint != nil {
		return false
	}

	n.openFilesMutex.Lock()
	defer n.openFilesMutex.Unlock()
	return len(n.openFiles) == 0
}

func (me *FileSystemConnector) renameUpdate(oldParent *Inode, oldName string, newParent *Inode, newName string) {
	defer me.verify()
	oldParent.treeLock.Lock()
	defer oldParent.treeLock.Unlock()

	if oldParent.mount != newParent.mount {
		panic("Cross mount rename")
	}

	node := oldParent.rmChild(oldName)
	if node == nil {
		panic("Source of rename does not exist")
	}
	newParent.rmChild(newName)
	newParent.addChild(newName, node)
}

func (me *FileSystemConnector) unlinkUpdate(parent *Inode, name string) {
	defer me.verify()

	parent.treeLock.Lock()
	defer parent.treeLock.Unlock()

	parent.rmChild(name)
}

// Walk the file system starting from the root. Will return nil if
// node not found.
func (me *FileSystemConnector) findLastKnownInode(fullPath string) (*Inode, []string) {
	if fullPath == "" {
		return me.rootNode, nil
	}

	fullPath = strings.TrimLeft(filepath.Clean(fullPath), "/")
	comps := strings.Split(fullPath, "/")

	node := me.rootNode
	for i, component := range comps {
		if len(component) == 0 {
			continue
		}

		if node.mountPoint != nil {
			node.mountPoint.treeLock.RLock()
			defer node.mountPoint.treeLock.RUnlock()
		}

		next := node.children[component]
		if next == nil {
			return node, comps[i:]
		}
		node = next
	}

	return node, nil
}

func (me *FileSystemConnector) findInode(fullPath string) *Inode {
	n, rest := me.findLastKnownInode(fullPath)
	if len(rest) > 0 {
		return nil
	}
	return n
}

////////////////////////////////////////////////////////////////

func (me *FileSystemConnector) MountRoot(nodeFs NodeFileSystem, opts *FileSystemOptions) {
	me.rootNode.mountFs(nodeFs, opts)
	nodeFs.OnMount(me)
	me.verify()
}

// Mount() generates a synthetic directory node, and mounts the file
// system there.  If opts is nil, the mount options of the root file
// system are inherited.  The encompassing filesystem should pretend
// the mount point does not exist.  If it does, it will generate an
// Inode with the same, which will cause Mount() to return EBUSY.
//
// Return values:
//
// ENOENT: the directory containing the mount point does not exist.
//
// EBUSY: the intended mount point already exists.
//
// TODO - would be useful to expose an interface to put all of the
// mount management in FileSystemConnector, so AutoUnionFs and
// MultiZipFs don't have to do it separately, with the risk of
// inconsistencies.
func (me *FileSystemConnector) Mount(parent *Inode, name string, nodeFs NodeFileSystem, opts *FileSystemOptions) Status {
	parent.treeLock.Lock()
	defer parent.treeLock.Unlock()
	node := parent.children[name]
	if node != nil {
		return EBUSY
	}

	node = me.newInode(true)
	if opts == nil {
		opts = me.rootNode.mountPoint.options
	}

	node.mountFs(nodeFs, opts)
	parent.addChild(name, node)

	if parent.mounts == nil {
		parent.mounts = make(map[string]*fileSystemMount)
	}
	parent.mounts[name] = node.mountPoint
	node.mountPoint.parentInode = parent
	if me.Debug {
		log.Println("Mount: ", nodeFs, "on subdir", name,
			"parent", parent.nodeId)
	}
	me.verify()
	nodeFs.OnMount(me)
	return OK
}

// Unmount() tries to unmount the given inode.
//
// Returns the following error codes:
//
// EINVAL: path does not exist, or is not a mount point.
//
// EBUSY: there are open files, or submounts below this node.
func (me *FileSystemConnector) Unmount(node *Inode) Status {
	if node.mountPoint == nil {
		log.Println("not a mountpoint:", node.nodeId)
		return EINVAL
	}

	// Must lock parent to update tree structure.
	parentNode := node.mountPoint.parentInode
	parentNode.treeLock.Lock()
	defer parentNode.treeLock.Unlock()

	mount := node.mountPoint
	name := node.mountPoint.mountName()
	if mount.openFiles.Count() > 0 {
		return EBUSY
	}

	mountInode := mount.mountInode
	if !mountInode.canUnmount() {
		return EBUSY
	}

	mount.mountInode = nil
	mountInode.mountPoint = nil

	parentNode.mounts[name] = nil, false
	parentNode.children[name] = nil, false
	mount.fs.OnUnmount()

	me.fsInit.EntryNotify(parentNode.nodeId, name)

	return OK
}

func (me *FileSystemConnector) FileNotify(path string, off int64, length int64) Status {
	node := me.findInode(path)
	if node == nil {
		return ENOENT
	}

	out := NotifyInvalInodeOut{
		Length: length,
		Off:    off,
		Ino:    node.nodeId,
	}
	return me.fsInit.InodeNotify(&out)
}

func (me *FileSystemConnector) EntryNotify(dir string, name string) Status {
	node := me.findInode(dir)
	if node == nil {
		return ENOENT
	}

	return me.fsInit.EntryNotify(node.nodeId, name)
}

func (me *FileSystemConnector) Notify(path string) Status {
	node, rest := me.findLastKnownInode(path)
	if len(rest) > 0 {
		return me.fsInit.EntryNotify(node.nodeId, rest[0])
	}
	out := NotifyInvalInodeOut{
		Ino: node.nodeId,
	}
	return me.fsInit.InodeNotify(&out)
}
<commit_msg>Document FileSystemConnector.createChild().<commit_after>package fuse

// This file contains the internal logic of the
// FileSystemConnector. The functions for satisfying the raw interface are in
// fsops.go

import (
	"fmt"
	"log"
	"os"
	"path/filepath"
	"strings"
	"unsafe"
)

// Tests should set to true.
var paranoia = false

func NewFileSystemOptions() *FileSystemOptions {
	return &FileSystemOptions{
		NegativeTimeout: 0.0,
		AttrTimeout:     1.0,
		EntryTimeout:    1.0,
		Owner:           CurrentOwner(),
	}
}

// FilesystemConnector is a raw FUSE filesystem that manages in-process mounts and inodes.
type FileSystemConnector struct {
	DefaultRawFileSystem

	Debug bool

	fsInit   RawFsInit
	inodeMap HandleMap
	rootNode *Inode
}

func NewFileSystemConnector(nodeFs NodeFileSystem, opts *FileSystemOptions) (me *FileSystemConnector) {
	me = new(FileSystemConnector)
	if opts == nil {
		opts = NewFileSystemOptions()
	}
	me.inodeMap = NewHandleMap(!opts.SkipCheckHandles)
	me.rootNode = me.newInode(true)
	me.rootNode.nodeId = FUSE_ROOT_ID

	// FUSE does not issue a LOOKUP for 1 (obviously), but it does
	// issue a forget.  This lookupCount is to make the counts match.
	me.rootNode.addLookupCount(1)
	me.verify()
	me.MountRoot(nodeFs, opts)
	return me
}

func (me *FileSystemConnector) verify() {
	if !paranoia {
		return
	}
	root := me.rootNode
	root.verify(me.rootNode.mountPoint)
}

func (me *FileSystemConnector) newInode(isDir bool) *Inode {
	data := new(Inode)
	data.nodeId = me.inodeMap.Register(&data.handled, data)
	data.connector = me
	if isDir {
		data.children = make(map[string]*Inode, initDirSize)
	}

	return data
}

// createChild() creates a child for given as FsNode as child of 'parent'.  The
// resulting inode will have its lookupCount incremented.
func (me *FileSystemConnector) createChild(parent *Inode, name string, fi *os.FileInfo, fsi FsNode) (out *EntryOut, child *Inode) {
	if fsi.Inode() == nil {
		child = parent.CreateChild(name, fi.IsDirectory(), fsi)
	} else {
		parent.treeLock.Lock()
		defer parent.treeLock.Unlock()
		child = fsi.Inode()
		child.addLookupCount(1)
	}

	out = parent.mount.fileInfoToEntry(fi)
	out.Ino = child.nodeId
	out.NodeId = child.nodeId
	return out, child
}

func (me *FileSystemConnector) lookupUpdate(parent *Inode, name string, isDir bool, lookupCount int) *Inode {
	defer me.verify()

	parent.treeLock.Lock()
	defer parent.treeLock.Unlock()

	data, ok := parent.children[name]
	if !ok {
		data = me.newInode(isDir)
		parent.addChild(name, data)
		data.mount = parent.mount
		data.treeLock = &data.mount.treeLock
	}
	data.addLookupCount(lookupCount)
	return data
}

func (me *FileSystemConnector) findMount(parent *Inode, name string) (mount *fileSystemMount) {
	parent.treeLock.RLock()
	defer parent.treeLock.RUnlock()
	if parent.mounts == nil {
		return nil
	}

	return parent.mounts[name]
}

func (me *FileSystemConnector) getInodeData(nodeid uint64) *Inode {
	if nodeid == FUSE_ROOT_ID {
		return me.rootNode
	}
	i := (*Inode)(unsafe.Pointer(DecodeHandle(nodeid)))
	return i
}

func (me *FileSystemConnector) forgetUpdate(nodeId uint64, forgetCount int) {
	defer me.verify()

	node := me.getInodeData(nodeId)

	node.treeLock.Lock()
	defer node.treeLock.Unlock()

	node.addLookupCount(-forgetCount)
	me.considerDropInode(node)
}

func (me *FileSystemConnector) considerDropInode(n *Inode) (drop bool) {
	delChildren := []string{}
	for k, v := range n.children {
		if v.mountPoint == nil && me.considerDropInode(v) {
			delChildren = append(delChildren, k)
		}
	}
	for _, k := range delChildren {
		ch := n.rmChild(k)
		if ch == nil {
			panic(fmt.Sprintf("trying to del child %q, but not present", k))
		}
		ch.fsInode.OnForget()
		me.inodeMap.Forget(ch.nodeId)
	}

	if len(n.children) > 0 || n.Live() {
		return false
	}
	if n == me.rootNode || n.mountPoint != nil {
		return false
	}

	n.openFilesMutex.Lock()
	defer n.openFilesMutex.Unlock()
	return len(n.openFiles) == 0
}

func (me *FileSystemConnector) renameUpdate(oldParent *Inode, oldName string, newParent *Inode, newName string) {
	defer me.verify()
	oldParent.treeLock.Lock()
	defer oldParent.treeLock.Unlock()

	if oldParent.mount != newParent.mount {
		panic("Cross mount rename")
	}

	node := oldParent.rmChild(oldName)
	if node == nil {
		panic("Source of rename does not exist")
	}
	newParent.rmChild(newName)
	newParent.addChild(newName, node)
}

func (me *FileSystemConnector) unlinkUpdate(parent *Inode, name string) {
	defer me.verify()

	parent.treeLock.Lock()
	defer parent.treeLock.Unlock()

	parent.rmChild(name)
}

// Walk the file system starting from the root. Will return nil if
// node not found.
func (me *FileSystemConnector) findLastKnownInode(fullPath string) (*Inode, []string) {
	if fullPath == "" {
		return me.rootNode, nil
	}

	fullPath = strings.TrimLeft(filepath.Clean(fullPath), "/")
	comps := strings.Split(fullPath, "/")

	node := me.rootNode
	for i, component := range comps {
		if len(component) == 0 {
			continue
		}

		if node.mountPoint != nil {
			node.mountPoint.treeLock.RLock()
			defer node.mountPoint.treeLock.RUnlock()
		}

		next := node.children[component]
		if next == nil {
			return node, comps[i:]
		}
		node = next
	}

	return node, nil
}

func (me *FileSystemConnector) findInode(fullPath string) *Inode {
	n, rest := me.findLastKnownInode(fullPath)
	if len(rest) > 0 {
		return nil
	}
	return n
}

////////////////////////////////////////////////////////////////

func (me *FileSystemConnector) MountRoot(nodeFs NodeFileSystem, opts *FileSystemOptions) {
	me.rootNode.mountFs(nodeFs, opts)
	nodeFs.OnMount(me)
	me.verify()
}

// Mount() generates a synthetic directory node, and mounts the file
// system there.  If opts is nil, the mount options of the root file
// system are inherited.  The encompassing filesystem should pretend
// the mount point does not exist.  If it does, it will generate an
// Inode with the same, which will cause Mount() to return EBUSY.
//
// Return values:
//
// ENOENT: the directory containing the mount point does not exist.
//
// EBUSY: the intended mount point already exists.
//
// TODO - would be useful to expose an interface to put all of the
// mount management in FileSystemConnector, so AutoUnionFs and
// MultiZipFs don't have to do it separately, with the risk of
// inconsistencies.
func (me *FileSystemConnector) Mount(parent *Inode, name string, nodeFs NodeFileSystem, opts *FileSystemOptions) Status {
	parent.treeLock.Lock()
	defer parent.treeLock.Unlock()
	node := parent.children[name]
	if node != nil {
		return EBUSY
	}

	node = me.newInode(true)
	if opts == nil {
		opts = me.rootNode.mountPoint.options
	}

	node.mountFs(nodeFs, opts)
	parent.addChild(name, node)

	if parent.mounts == nil {
		parent.mounts = make(map[string]*fileSystemMount)
	}
	parent.mounts[name] = node.mountPoint
	node.mountPoint.parentInode = parent
	if me.Debug {
		log.Println("Mount: ", nodeFs, "on subdir", name,
			"parent", parent.nodeId)
	}
	me.verify()
	nodeFs.OnMount(me)
	return OK
}

// Unmount() tries to unmount the given inode.
//
// Returns the following error codes:
//
// EINVAL: path does not exist, or is not a mount point.
//
// EBUSY: there are open files, or submounts below this node.
func (me *FileSystemConnector) Unmount(node *Inode) Status {
	if node.mountPoint == nil {
		log.Println("not a mountpoint:", node.nodeId)
		return EINVAL
	}

	// Must lock parent to update tree structure.
	parentNode := node.mountPoint.parentInode
	parentNode.treeLock.Lock()
	defer parentNode.treeLock.Unlock()

	mount := node.mountPoint
	name := node.mountPoint.mountName()
	if mount.openFiles.Count() > 0 {
		return EBUSY
	}

	mountInode := mount.mountInode
	if !mountInode.canUnmount() {
		return EBUSY
	}

	mount.mountInode = nil
	mountInode.mountPoint = nil

	parentNode.mounts[name] = nil, false
	parentNode.children[name] = nil, false
	mount.fs.OnUnmount()

	me.fsInit.EntryNotify(parentNode.nodeId, name)

	return OK
}

func (me *FileSystemConnector) FileNotify(path string, off int64, length int64) Status {
	node := me.findInode(path)
	if node == nil {
		return ENOENT
	}

	out := NotifyInvalInodeOut{
		Length: length,
		Off:    off,
		Ino:    node.nodeId,
	}
	return me.fsInit.InodeNotify(&out)
}

func (me *FileSystemConnector) EntryNotify(dir string, name string) Status {
	node := me.findInode(dir)
	if node == nil {
		return ENOENT
	}

	return me.fsInit.EntryNotify(node.nodeId, name)
}

func (me *FileSystemConnector) Notify(path string) Status {
	node, rest := me.findLastKnownInode(path)
	if len(rest) > 0 {
		return me.fsInit.EntryNotify(node.nodeId, rest[0])
	}
	out := NotifyInvalInodeOut{
		Ino: node.nodeId,
	}
	return me.fsInit.InodeNotify(&out)
}
<|endoftext|>


<commit_before>// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://gitee.com/johng/gf.

// 视图管理.
package gview

import (
    "fmt"
    "gitee.com/johng/gf/g/encoding/gurl"
    "gitee.com/johng/gf/g/os/glog"
    "gitee.com/johng/gf/g/os/gtime"
    "gitee.com/johng/gf/g/util/gstr"
    "strings"
    "sync"
    "bytes"
    "errors"
    "text/template"
    "gitee.com/johng/gf/g/container/gmap"
    "gitee.com/johng/gf/g/encoding/ghash"
    "gitee.com/johng/gf/g/util/gconv"
    "gitee.com/johng/gf/g/os/gspath"
    "gitee.com/johng/gf/g/os/gfcache"
    "gitee.com/johng/gf/g/encoding/ghtml"
)

// 视图对象
type View struct {
    mu         sync.RWMutex
    paths      *gspath.SPath           // 模板查找目录(绝对路径)
    data       map[string]interface{}  // 模板变量
    funcmap    map[string]interface{}  // FuncMap
    delimiters []string                // 模板变量分隔符号
}

// 模板变量
type Params  = map[string]interface{}

// 函数映射表
type FuncMap = map[string]interface{}

// 视图表
var viewMap = gmap.NewStringInterfaceMap()

// 默认的视图对象
var viewObj *View

// 初始化默认的视图对象
func checkAndInitDefaultView() {
    if viewObj == nil {
        viewObj = Get(".")
    }
}

// 直接解析模板内容，返回解析后的内容
func ParseContent(content string, params Params) ([]byte, error) {
    checkAndInitDefaultView()
    return viewObj.ParseContent(content, params)
}

// 获取或者创建一个视图对象
func Get(path string) *View {
    if r := viewMap.Get(path); r != nil {
        return r.(*View)
    }
    v := New(path)
    viewMap.Set(path, v)
    return v
}

// 生成一个视图对象
func New(path string) *View {
    view := &View {
        paths      : gspath.New(),
        data       : make(map[string]interface{}),
        funcmap    : make(map[string]interface{}),
        delimiters : make([]string, 2),
    }
    view.SetPath(path)
    view.SetDelimiters("{{", "}}")
    // 内置方法
    view.BindFunc("text",        view.funcText)
    view.BindFunc("html",        view.funcHtmlEncode)
    view.BindFunc("htmlencode",  view.funcHtmlEncode)
    view.BindFunc("htmldecode",  view.funcHtmlDecode)
    view.BindFunc("url",         view.funcUrlEncode)
    view.BindFunc("urlencode",   view.funcUrlEncode)
    view.BindFunc("urldecode",   view.funcUrlDecode)
    view.BindFunc("date",        view.funcDate)
    view.BindFunc("substr",      view.funcSubStr)
    view.BindFunc("strlimit",    view.funcStrLimit)
    view.BindFunc("compare",     view.funcCompare)
    view.BindFunc("hidestr",     view.funcHideStr)
    view.BindFunc("highlight",   view.funcHighlight)
    view.BindFunc("toupper",     view.funcToUpper)
    view.BindFunc("tolower",     view.funcToLower)
    view.BindFunc("nl2br",       view.funcNl2Br)
    view.BindFunc("include",     view.funcInclude)
    return view
}

// 设置模板目录绝对路径
func (view *View) SetPath(path string) error {
    if rp, err := view.paths.Set(path); err != nil {
        glog.Error("gview.SetPath failed:", err.Error())
        return err
    } else {
        glog.Debug("gview.SetPath:", rp)
    }
    return nil
}

// 添加模板目录搜索路径
func (view *View) AddPath(path string) error {
    if rp, err := view.paths.Add(path); err != nil {
        glog.Error("gview.AddPath failed:", err.Error())
        return err
    } else {
        glog.Debug("gview.SetPath:", rp)
    }
    return nil
}

// 批量绑定模板变量，即调用之后每个线程都会生效，因此有并发安全控制
func (view *View) Assigns(data Params) {
    view.mu.Lock()
    for k, v := range data {
        view.data[k] = v
    }
    view.mu.Unlock()
}

// 绑定模板变量，即调用之后每个线程都会生效，因此有并发安全控制
func (view *View) Assign(key string, value interface{}) {
    view.mu.Lock()
    view.data[key] = value
    view.mu.Unlock()
}

// 解析模板，返回解析后的内容
func (view *View) Parse(file string, params Params, funcmap...map[string]interface{}) ([]byte, error) {
    path := view.paths.Search(file)
    if path == "" {
        return nil, errors.New("tpl \"" + file + "\" not found")
    }
    content := gfcache.GetContents(path)
    // 执行模板解析，互斥锁主要是用于funcmap
    view.mu.RLock()
    defer view.mu.RUnlock()
    buffer := bytes.NewBuffer(nil)
    tplobj := template.New(path).Delims(view.delimiters[0], view.delimiters[1]).Funcs(view.funcmap)
    if len(funcmap) > 0 {
        tplobj = tplobj.Funcs(funcmap[0])
    }
    if tpl, err := tplobj.Parse(content); err != nil {
        return nil, err
    } else {
        // 注意模板变量赋值不能改变已有的params或者view.data的值，因为这两个变量都是指针
        // 因此在必要条件下，需要合并两个map的值到一个新的map
        vars := (map[string]interface{})(nil)
        if len(view.data) > 0 {
            if len(params) > 0 {
                vars = make(map[string]interface{}, len(view.data) + len(params))
                for k, v := range params {
                    vars[k] = v
                }
                for k, v := range view.data {
                    vars[k] = v
                }
            } else {
                vars = view.data
            }
        } else {
            vars = params
        }
        if err := tpl.Execute(buffer, vars); err != nil {
            return nil, err
        }
    }
    return buffer.Bytes(), nil
}

// 直接解析模板内容，返回解析后的内容
func (view *View) ParseContent(content string, params Params, funcmap...map[string]interface{}) ([]byte, error) {
    view.mu.RLock()
    defer view.mu.RUnlock()
    name   := gconv.String(ghash.BKDRHash64([]byte(content)))
    buffer := bytes.NewBuffer(nil)
    tplobj := template.New(name).Delims(view.delimiters[0], view.delimiters[1]).Funcs(view.funcmap)
    if len(funcmap) > 0 {
        tplobj = tplobj.Funcs(funcmap[0])
    }
    if tpl, err := tplobj.Parse(content); err != nil {
        return nil, err
    } else {
        // 注意模板变量赋值不能改变已有的params或者view.data的值，因为这两个变量都是指针
        // 因此在必要条件下，需要合并两个map的值到一个新的map
        vars := (map[string]interface{})(nil)
        if len(view.data) > 0 {
            if len(params) > 0 {
                vars = make(map[string]interface{}, len(view.data) + len(params))
                for k, v := range params {
                    vars[k] = v
                }
                for k, v := range view.data {
                    vars[k] = v
                }
            } else {
                vars = view.data
            }
        } else {
            vars = params
        }
        if err := tpl.Execute(buffer, vars); err != nil {
            return nil, err
        }
    }
    return buffer.Bytes(), nil
}

// 设置模板变量解析分隔符号
func (view *View) SetDelimiters(left, right string) {
    view.delimiters[0] = left
    view.delimiters[1] = right
}

// 绑定自定义函数，该函数是全局有效，即调用之后每个线程都会生效，因此有并发安全控制
func (view *View) BindFunc(name string, function interface{}) {
    view.mu.Lock()
    view.funcmap[name] = function
    view.mu.Unlock()
}

// 模板内置方法：include
func (view *View) funcInclude(file string, data...map[string]interface{}) string {
    var m map[string]interface{} = nil
    if len(data) > 0 {
        m = data[0]
    }
    content, err := view.Parse(file, m)
    if err != nil {
        return err.Error()
    }
    return string(content)
}

// 模板内置方法：text
func (view *View) funcText(html interface{}) string {
    return ghtml.StripTags(gconv.String(html))
}

// 模板内置方法：html
func (view *View) funcHtmlEncode(html interface{}) string {
    return ghtml.Entities(gconv.String(html))
}

// 模板内置方法：htmldecode
func (view *View) funcHtmlDecode(html interface{}) string {
    return ghtml.EntitiesDecode(gconv.String(html))
}

// 模板内置方法：url
func (view *View) funcUrlEncode(url interface{}) string {
    return gurl.Encode(gconv.String(url))
}

// 模板内置方法：urldecode
func (view *View) funcUrlDecode(url interface{}) string {
    if content, err := gurl.Decode(gconv.String(url)); err == nil {
        return content
    } else {
        return err.Error()
    }
}

// 模板内置方法：date
func (view *View) funcDate(format string, timestamp interface{}) string {
    return gtime.NewFromTimeStamp(gconv.Int64(timestamp)).Format(format)
}

// 模板内置方法：compare
func (view *View) funcCompare(value1, value2 interface{}) int {
    return strings.Compare(gconv.String(value1), gconv.String(value2))
}

// 模板内置方法：substr
func (view *View) funcSubStr(start, end int, str interface{}) string {
    return gstr.SubStr(gconv.String(str), start, end)
}

// 模板内置方法：strlimit
func (view *View) funcStrLimit(length int, suffix string, str interface{}) string {
    return gstr.StrLimit(gconv.String(str), length, suffix)
}

// 模板内置方法：highlight
func (view *View) funcHighlight(key string, color string, str interface{}) string {
    return gstr.Replace(gconv.String(str), key, fmt.Sprintf(`<span style="color:%s;">%s</span>`, color, key))
}

// 模板内置方法：hidestr
func (view *View) funcHideStr(percent int, hide string, str interface{}) string {
    return gstr.HideStr(gconv.String(str), percent, hide)
}

// 模板内置方法：toupper
func (view *View) funcToUpper(str interface{}) string {
    return gstr.ToUpper(gconv.String(str))
}

// 模板内置方法：toupper
func (view *View) funcToLower(str interface{}) string {
    return gstr.ToLower(gconv.String(str))
}

// 模板内置方法：nl2br
func (view *View) funcNl2Br(str interface{}) string {
    return gstr.Nl2Br(gconv.String(str))
}

<commit_msg>修改gview调试信息<commit_after>// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://gitee.com/johng/gf.

// 视图管理.
package gview

import (
    "fmt"
    "gitee.com/johng/gf/g/encoding/gurl"
    "gitee.com/johng/gf/g/os/glog"
    "gitee.com/johng/gf/g/os/gtime"
    "gitee.com/johng/gf/g/util/gstr"
    "strings"
    "sync"
    "bytes"
    "errors"
    "text/template"
    "gitee.com/johng/gf/g/container/gmap"
    "gitee.com/johng/gf/g/encoding/ghash"
    "gitee.com/johng/gf/g/util/gconv"
    "gitee.com/johng/gf/g/os/gspath"
    "gitee.com/johng/gf/g/os/gfcache"
    "gitee.com/johng/gf/g/encoding/ghtml"
)

// 视图对象
type View struct {
    mu         sync.RWMutex
    paths      *gspath.SPath           // 模板查找目录(绝对路径)
    data       map[string]interface{}  // 模板变量
    funcmap    map[string]interface{}  // FuncMap
    delimiters []string                // 模板变量分隔符号
}

// 模板变量
type Params  = map[string]interface{}

// 函数映射表
type FuncMap = map[string]interface{}

// 视图表
var viewMap = gmap.NewStringInterfaceMap()

// 默认的视图对象
var viewObj *View

// 初始化默认的视图对象
func checkAndInitDefaultView() {
    if viewObj == nil {
        viewObj = Get(".")
    }
}

// 直接解析模板内容，返回解析后的内容
func ParseContent(content string, params Params) ([]byte, error) {
    checkAndInitDefaultView()
    return viewObj.ParseContent(content, params)
}

// 获取或者创建一个视图对象
func Get(path string) *View {
    if r := viewMap.Get(path); r != nil {
        return r.(*View)
    }
    v := New(path)
    viewMap.Set(path, v)
    return v
}

// 生成一个视图对象
func New(path string) *View {
    view := &View {
        paths      : gspath.New(),
        data       : make(map[string]interface{}),
        funcmap    : make(map[string]interface{}),
        delimiters : make([]string, 2),
    }
    view.SetPath(path)
    view.SetDelimiters("{{", "}}")
    // 内置方法
    view.BindFunc("text",        view.funcText)
    view.BindFunc("html",        view.funcHtmlEncode)
    view.BindFunc("htmlencode",  view.funcHtmlEncode)
    view.BindFunc("htmldecode",  view.funcHtmlDecode)
    view.BindFunc("url",         view.funcUrlEncode)
    view.BindFunc("urlencode",   view.funcUrlEncode)
    view.BindFunc("urldecode",   view.funcUrlDecode)
    view.BindFunc("date",        view.funcDate)
    view.BindFunc("substr",      view.funcSubStr)
    view.BindFunc("strlimit",    view.funcStrLimit)
    view.BindFunc("compare",     view.funcCompare)
    view.BindFunc("hidestr",     view.funcHideStr)
    view.BindFunc("highlight",   view.funcHighlight)
    view.BindFunc("toupper",     view.funcToUpper)
    view.BindFunc("tolower",     view.funcToLower)
    view.BindFunc("nl2br",       view.funcNl2Br)
    view.BindFunc("include",     view.funcInclude)
    return view
}

// 设置模板目录绝对路径
func (view *View) SetPath(path string) error {
    if rp, err := view.paths.Set(path); err != nil {
        glog.Error("gview.SetPath failed:", err.Error())
        return err
    } else {
        glog.Debug("gview.SetPath:", rp)
    }
    return nil
}

// 添加模板目录搜索路径
func (view *View) AddPath(path string) error {
    if rp, err := view.paths.Add(path); err != nil {
        glog.Error("gview.AddPath failed:", err.Error())
        return err
    } else {
        glog.Debug("gview.AddPath:", rp)
    }
    return nil
}

// 批量绑定模板变量，即调用之后每个线程都会生效，因此有并发安全控制
func (view *View) Assigns(data Params) {
    view.mu.Lock()
    for k, v := range data {
        view.data[k] = v
    }
    view.mu.Unlock()
}

// 绑定模板变量，即调用之后每个线程都会生效，因此有并发安全控制
func (view *View) Assign(key string, value interface{}) {
    view.mu.Lock()
    view.data[key] = value
    view.mu.Unlock()
}

// 解析模板，返回解析后的内容
func (view *View) Parse(file string, params Params, funcmap...map[string]interface{}) ([]byte, error) {
    path := view.paths.Search(file)
    if path == "" {
        return nil, errors.New("tpl \"" + file + "\" not found")
    }
    content := gfcache.GetContents(path)
    // 执行模板解析，互斥锁主要是用于funcmap
    view.mu.RLock()
    defer view.mu.RUnlock()
    buffer := bytes.NewBuffer(nil)
    tplobj := template.New(path).Delims(view.delimiters[0], view.delimiters[1]).Funcs(view.funcmap)
    if len(funcmap) > 0 {
        tplobj = tplobj.Funcs(funcmap[0])
    }
    if tpl, err := tplobj.Parse(content); err != nil {
        return nil, err
    } else {
        // 注意模板变量赋值不能改变已有的params或者view.data的值，因为这两个变量都是指针
        // 因此在必要条件下，需要合并两个map的值到一个新的map
        vars := (map[string]interface{})(nil)
        if len(view.data) > 0 {
            if len(params) > 0 {
                vars = make(map[string]interface{}, len(view.data) + len(params))
                for k, v := range params {
                    vars[k] = v
                }
                for k, v := range view.data {
                    vars[k] = v
                }
            } else {
                vars = view.data
            }
        } else {
            vars = params
        }
        if err := tpl.Execute(buffer, vars); err != nil {
            return nil, err
        }
    }
    return buffer.Bytes(), nil
}

// 直接解析模板内容，返回解析后的内容
func (view *View) ParseContent(content string, params Params, funcmap...map[string]interface{}) ([]byte, error) {
    view.mu.RLock()
    defer view.mu.RUnlock()
    name   := gconv.String(ghash.BKDRHash64([]byte(content)))
    buffer := bytes.NewBuffer(nil)
    tplobj := template.New(name).Delims(view.delimiters[0], view.delimiters[1]).Funcs(view.funcmap)
    if len(funcmap) > 0 {
        tplobj = tplobj.Funcs(funcmap[0])
    }
    if tpl, err := tplobj.Parse(content); err != nil {
        return nil, err
    } else {
        // 注意模板变量赋值不能改变已有的params或者view.data的值，因为这两个变量都是指针
        // 因此在必要条件下，需要合并两个map的值到一个新的map
        vars := (map[string]interface{})(nil)
        if len(view.data) > 0 {
            if len(params) > 0 {
                vars = make(map[string]interface{}, len(view.data) + len(params))
                for k, v := range params {
                    vars[k] = v
                }
                for k, v := range view.data {
                    vars[k] = v
                }
            } else {
                vars = view.data
            }
        } else {
            vars = params
        }
        if err := tpl.Execute(buffer, vars); err != nil {
            return nil, err
        }
    }
    return buffer.Bytes(), nil
}

// 设置模板变量解析分隔符号
func (view *View) SetDelimiters(left, right string) {
    view.delimiters[0] = left
    view.delimiters[1] = right
}

// 绑定自定义函数，该函数是全局有效，即调用之后每个线程都会生效，因此有并发安全控制
func (view *View) BindFunc(name string, function interface{}) {
    view.mu.Lock()
    view.funcmap[name] = function
    view.mu.Unlock()
}

// 模板内置方法：include
func (view *View) funcInclude(file string, data...map[string]interface{}) string {
    var m map[string]interface{} = nil
    if len(data) > 0 {
        m = data[0]
    }
    content, err := view.Parse(file, m)
    if err != nil {
        return err.Error()
    }
    return string(content)
}

// 模板内置方法：text
func (view *View) funcText(html interface{}) string {
    return ghtml.StripTags(gconv.String(html))
}

// 模板内置方法：html
func (view *View) funcHtmlEncode(html interface{}) string {
    return ghtml.Entities(gconv.String(html))
}

// 模板内置方法：htmldecode
func (view *View) funcHtmlDecode(html interface{}) string {
    return ghtml.EntitiesDecode(gconv.String(html))
}

// 模板内置方法：url
func (view *View) funcUrlEncode(url interface{}) string {
    return gurl.Encode(gconv.String(url))
}

// 模板内置方法：urldecode
func (view *View) funcUrlDecode(url interface{}) string {
    if content, err := gurl.Decode(gconv.String(url)); err == nil {
        return content
    } else {
        return err.Error()
    }
}

// 模板内置方法：date
func (view *View) funcDate(format string, timestamp interface{}) string {
    return gtime.NewFromTimeStamp(gconv.Int64(timestamp)).Format(format)
}

// 模板内置方法：compare
func (view *View) funcCompare(value1, value2 interface{}) int {
    return strings.Compare(gconv.String(value1), gconv.String(value2))
}

// 模板内置方法：substr
func (view *View) funcSubStr(start, end int, str interface{}) string {
    return gstr.SubStr(gconv.String(str), start, end)
}

// 模板内置方法：strlimit
func (view *View) funcStrLimit(length int, suffix string, str interface{}) string {
    return gstr.StrLimit(gconv.String(str), length, suffix)
}

// 模板内置方法：highlight
func (view *View) funcHighlight(key string, color string, str interface{}) string {
    return gstr.Replace(gconv.String(str), key, fmt.Sprintf(`<span style="color:%s;">%s</span>`, color, key))
}

// 模板内置方法：hidestr
func (view *View) funcHideStr(percent int, hide string, str interface{}) string {
    return gstr.HideStr(gconv.String(str), percent, hide)
}

// 模板内置方法：toupper
func (view *View) funcToUpper(str interface{}) string {
    return gstr.ToUpper(gconv.String(str))
}

// 模板内置方法：toupper
func (view *View) funcToLower(str interface{}) string {
    return gstr.ToLower(gconv.String(str))
}

// 模板内置方法：nl2br
func (view *View) funcNl2Br(str interface{}) string {
    return gstr.Nl2Br(gconv.String(str))
}

<|endoftext|>


<commit_before>package ssh

import (
	"crypto"
	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"
	"errors"
	"io"
)

type Keychain struct {
	keys []interface{}
}

func (k *Keychain) Add(privateKey []byte) error {
	block, _ := pem.Decode(privateKey)

	if block == nil {
		return errors.New("no PEM data found")
	}

	key, err := x509.ParsePKCS1PrivateKey(block.Bytes)

	if err != nil {
		return err
	}

	k.keys = append(k.keys, key)

	return nil
}

func (k *Keychain) Key(i int) (interface{}, error) {
	if i < 0 || i >= len(k.keys) {
		return nil, nil
	}

	switch key := k.keys[i].(type) {
	case *rsa.PrivateKey:
		return &key.PublicKey, nil
	}
	panic("unknown key type")
}

func (k *Keychain) Sign(i int, rand io.Reader, data []byte) ([]byte, error) {
	h := crypto.SHA1.New()
	h.Write(data)
	digest := h.Sum(nil)

	switch key := k.keys[i].(type) {
	case *rsa.PrivateKey:
		return rsa.SignPKCS1v15(rand, key, crypto.SHA1, digest)
	}

	return nil, errors.New("unknown key type")
}
<commit_msg>Updating to reflect latest in the SSH package.<commit_after>package ssh

import (
	"code.google.com/p/go.crypto/ssh"
	"io"
)

type Keychain struct {
	keys []ssh.Signer
}

func (k *Keychain) Add(privateKey []byte) error {
	key, err := ssh.ParsePrivateKey(privateKey)

	if err != nil {
		return err
	}

	k.keys = append(k.keys, key)

	return nil
}

func (k *Keychain) Key(i int) ssh.PublicKey {
	if i < 0 || i >= len(k.keys) {
		return nil
	}

	return k.keys[i].PublicKey()
}

func (k *Keychain) Sign(i int, rand io.Reader, data []byte) ([]byte, error) {
	return k.keys[i].Sign(rand, data)
}
<|endoftext|>


<commit_before>package templater

import (
	"io"
	"strings"
	"fmt"
	"bytes"
	"sync"

	"github.com/valyala/fasttemplate"
	"github.com/InnovaCo/serve/utils/gabs"
)

var bytesBufferPool = sync.Pool{New: func() interface{} {
	return &bytes.Buffer{}
}}

const TIMES = 42

func Template(s string, context *gabs.Container) (string, error) {
	return _templater(s, context, true)
}

func MatchTemplate(s string, context *gabs.Container) (string, error) {
	return _templater(s, context, false)
}

func _templater(s string, context *gabs.Container, modify bool) (string, error) {
	var result = s
	var err error = nil

	for i := 0; i < TIMES; i++ {
		if result, err = _template(result, context, modify); err != nil {
			return "", err
		}
		if !(strings.Contains(result, "{{") && strings.Contains(result, "}}")) {
			break
		}
	}
	return result, nil
}

func _template(s string, context *gabs.Container, modify bool) (string, error) {
	t, err := fasttemplate.NewTemplate(s, "{{", "}}")
	if err != nil {
		return "", err
	}

	w := bytesBufferPool.Get().(*bytes.Buffer)

	if _, err := t.ExecuteFunc(w, func(w io.Writer, tag string) (int, error) {
		tag = strings.TrimSpace(tag)
		if value := context.Path(tag).Data(); value != nil {
			return w.Write([]byte(fmt.Sprintf("%v", value)))
		} else if modify {
			if v, err := ModifyExec(tag, context); err == nil {
				return w.Write([]byte(fmt.Sprintf("%v", v)))
			}
		}

		if strings.HasPrefix(tag, "vars.") || context.ExistsP(tag) {
			return 0, nil
		}

		return 0, fmt.Errorf("Undefined template variable: '%s'", tag)
	}); err != nil {
		return "", err
	}

	out := string(w.Bytes())
	w.Reset()
	bytesBufferPool.Put(w)

	return out, nil
}
<commit_msg>= fix: apply modificators only if template containts |<commit_after>package templater

import (
	"io"
	"strings"
	"fmt"
	"bytes"
	"sync"

	"github.com/valyala/fasttemplate"
	"github.com/InnovaCo/serve/utils/gabs"
)

var bytesBufferPool = sync.Pool{New: func() interface{} {
	return &bytes.Buffer{}
}}

const TIMES = 42

func Template(s string, context *gabs.Container) (string, error) {
	return _templater(s, context, true)
}

func MatchTemplate(s string, context *gabs.Container) (string, error) {
	return _templater(s, context, false)
}

func _templater(s string, context *gabs.Container, modify bool) (string, error) {
	var result = s
	var err error = nil

	for i := 0; i < TIMES; i++ {
		if result, err = _template(result, context, modify); err != nil {
			return "", err
		}
		if !(strings.Contains(result, "{{") && strings.Contains(result, "}}")) {
			break
		}
	}
	return result, nil
}

func _template(s string, context *gabs.Container, modify bool) (string, error) {
	t, err := fasttemplate.NewTemplate(s, "{{", "}}")
	if err != nil {
		return "", err
	}

	w := bytesBufferPool.Get().(*bytes.Buffer)

	if _, err := t.ExecuteFunc(w, func(w io.Writer, tag string) (int, error) {
		tag = strings.TrimSpace(tag)
		if value := context.Path(tag).Data(); value != nil {
			return w.Write([]byte(fmt.Sprintf("%v", value)))
		} else if modify && strings.Contains(tag, "|") {
			if v, err := ModifyExec(tag, context); err == nil {
				return w.Write([]byte(fmt.Sprintf("%v", v)))
			}
		}

		if strings.HasPrefix(tag, "vars.") || context.ExistsP(tag) {
			return 0, nil
		}

		return 0, fmt.Errorf("Undefined template variable: '%s'", tag)
	}); err != nil {
		return "", err
	}

	out := string(w.Bytes())
	w.Reset()
	bytesBufferPool.Put(w)

	return out, nil
}
<|endoftext|>


<commit_before>package update

import (
	"fmt"
	"time"

	"github.com/jcelliott/lumber"

	"github.com/nanobox-io/nanobox/models"
)

// Check for updates to nanobox every other day
const checkFrequency = (48 * time.Hour)

// Check checks to see if there is an update available for the nanobox CLI
func Check() {
	// load the update model
	updateInfo, err := models.LoadUpdate()
	if err != nil {
		lumber.Error("update:models.LoadUpdate(): %s", err)
		return
	}

	// return early if it's not time to check yet
	if !checkable(updateInfo) {
		return
	}

	latest := latestVersion()
	if latest == "" {
		checkTomorrow(updateInfo)
		return
	}
	lumber.Debug("CurrVers: %s\nLatest:   %s\n", models.VersionString(), latest)

	// if the versions don't match and it's 'time' for an update (14 days) inform the
	// user that updates are available
	if models.VersionString() != latest {
		//
		fmt.Printf(`
------------------------------------------------
Hey! A newer version of nanobox is available.

  %s

Run the following command to update:

$ nanobox-update
------------------------------------------------
`, latest)

	}

	// renew the update last checked time
	updateInfo.LastCheckAt = time.Now()
	if err := updateInfo.Save(); err != nil {
		lumber.Error("update:updateInfo.Save(): %s", err)
	}
}

// Checkable determines if the update has expired based on the check frequency
func checkable(updateInfo *models.Update) bool {
	return time.Since(updateInfo.LastCheckAt) >= checkFrequency
}

// CheckTomorrow updates the last checked at time to a day later so nanobox will
// check for updates tommorrow. This is only called if the check failed, likely
// due to a network error.
func checkTomorrow(updateInfo *models.Update) error {
	updateInfo.LastCheckAt = updateInfo.LastCheckAt.Add(24 * time.Hour)
	return updateInfo.Save()
}
<commit_msg>Record install as 'last update'<commit_after>package update

import (
	"fmt"
	"strings"
	"time"

	"github.com/jcelliott/lumber"

	"github.com/nanobox-io/nanobox/models"
)

// Check for updates to nanobox every other day
const checkFrequency = (48 * time.Hour)

// Check checks to see if there is an update available for the nanobox CLI
func Check() {
	// load the update model
	updateInfo, err := models.LoadUpdate()
	if err != nil {
		if strings.Contains(err.Error(), "no record found") {
			checkTomorrow(&models.Update{})
			return
		}
		lumber.Error("update:models.LoadUpdate(): %s", err)
		return
	}

	// return early if it's not time to check yet
	if !checkable(updateInfo) {
		return
	}

	latest := latestVersion()
	if latest == "" {
		checkTomorrow(updateInfo)
		return
	}
	lumber.Debug("CurrVers: %s\nLatest:   %s\n", models.VersionString(), latest)

	// if the versions don't match and it's 'time' for an update (14 days) inform the
	// user that updates are available
	if models.VersionString() != latest {
		//
		fmt.Printf(`
------------------------------------------------
Hey! A newer version of nanobox is available.

  %s

Run the following command to update:

$ nanobox-update
------------------------------------------------
`, latest)

	}

	// renew the update last checked time
	updateInfo.LastCheckAt = time.Now()
	if err := updateInfo.Save(); err != nil {
		lumber.Error("update:updateInfo.Save(): %s", err)
	}
}

// Checkable determines if the update has expired based on the check frequency
func checkable(updateInfo *models.Update) bool {
	return time.Since(updateInfo.LastCheckAt) >= checkFrequency
}

// CheckTomorrow updates the last checked at time to a day later so nanobox will
// check for updates tommorrow. This is only called if the check failed, likely
// due to a network error.
func checkTomorrow(updateInfo *models.Update) error {
	updateInfo.LastCheckAt = updateInfo.LastCheckAt.Add(24 * time.Hour)
	return updateInfo.Save()
}
<|endoftext|>


<commit_before>package version

// VERSION ...
const VERSION = "1.1.30"
<commit_msg>v1.1.31<commit_after>package version

// VERSION ...
const VERSION = "1.1.31"
<|endoftext|>


<commit_before>// Copyright 2017 Istio 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 version for fortio holds version information and build information.
package version // import "istio.io/fortio/version"
import (
	"fmt"

	"istio.io/fortio/log"
)

const (
	major = 0
	minor = 7
	patch = 1

	debug = false // turn on to debug init()
)

var (
	// The following are set by Dockerfile during link time:
	tag       = "pre"
	buildInfo = "unknown"
	// Number of lines in git status --porcelain; 0 means clean
	gitstatus = "0" // buildInfo default is unknown so no need to add -dirty
	// computed in init()
	version     = ""
	longVersion = ""
)

// Major returns the numerical major version number (first digit of Version()).
func Major() int {
	return major
}

// Minor returns the numerical minor version number (second digit of Version()).
func Minor() int {
	return minor
}

// Patch returns the numerical patch level (third digit of Version()).
func Patch() int {
	return patch
}

// Short returns the 3 digit short version string Major.Minor.Patch[-build]
// version.Short() is the overall project version (used to version json
// output too).
func Short() string {
	return version
}

// Long returns the full version and build information.
func Long() string {
	return longVersion
}

// Carefully manually tested all the combinations in pair with Dockerfile
func init() {
	if debug {
		log.SetLogLevel(log.Debug)
	}
	version = fmt.Sprintf("%d.%d.%d", major, minor, patch)
	clean := (gitstatus == "0")
	// The docker build will pass the git tag to the build, if it is clean
	// from a tag it will look like v0.7.0
	if tag != "v"+version || !clean {
		log.Debugf("tag is %v, clean is %v marking as pre release", tag, clean)
		version += "-pre"
	}
	if !clean {
		buildInfo += "-dirty"
		log.Debugf("gitstatus is %q, marking buildinfo as dirty: %v", gitstatus, buildInfo)
	}
	longVersion = version + "-" + buildInfo
}
<commit_msg>patch level bump for future 0.7.2<commit_after>// Copyright 2017 Istio 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 version for fortio holds version information and build information.
package version // import "istio.io/fortio/version"
import (
	"fmt"

	"istio.io/fortio/log"
)

const (
	major = 0
	minor = 7
	patch = 2

	debug = false // turn on to debug init()
)

var (
	// The following are set by Dockerfile during link time:
	tag       = "pre"
	buildInfo = "unknown"
	// Number of lines in git status --porcelain; 0 means clean
	gitstatus = "0" // buildInfo default is unknown so no need to add -dirty
	// computed in init()
	version     = ""
	longVersion = ""
)

// Major returns the numerical major version number (first digit of Version()).
func Major() int {
	return major
}

// Minor returns the numerical minor version number (second digit of Version()).
func Minor() int {
	return minor
}

// Patch returns the numerical patch level (third digit of Version()).
func Patch() int {
	return patch
}

// Short returns the 3 digit short version string Major.Minor.Patch[-build]
// version.Short() is the overall project version (used to version json
// output too).
func Short() string {
	return version
}

// Long returns the full version and build information.
func Long() string {
	return longVersion
}

// Carefully manually tested all the combinations in pair with Dockerfile
func init() {
	if debug {
		log.SetLogLevel(log.Debug)
	}
	version = fmt.Sprintf("%d.%d.%d", major, minor, patch)
	clean := (gitstatus == "0")
	// The docker build will pass the git tag to the build, if it is clean
	// from a tag it will look like v0.7.0
	if tag != "v"+version || !clean {
		log.Debugf("tag is %v, clean is %v marking as pre release", tag, clean)
		version += "-pre"
	}
	if !clean {
		buildInfo += "-dirty"
		log.Debugf("gitstatus is %q, marking buildinfo as dirty: %v", gitstatus, buildInfo)
	}
	longVersion = version + "-" + buildInfo
}
<|endoftext|>


<commit_before>// Copyright 2015 ThoughtWorks, Inc.

// This file is part of Gauge.

// Gauge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either Version 3 of the License, or
// (at your option) any later Version.

// Gauge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Gauge.  If not, see <http://www.gnu.org/licenses/>.

package version

import (
	"fmt"
	"sort"
	"strconv"
	"strings"
)

// CurrentGaugeVersion represents the current version of Gauge
var CurrentGaugeVersion = &Version{0, 8, 4}

// BuildMetadata represents build information of current release (e.g, nightly build information)
var BuildMetadata = ""

type Version struct {
	Major int
	Minor int
	Patch int
}

type VersionSupport struct {
	Minimum string
	Maximum string
}

func ParseVersion(versionText string) (*Version, error) {
	splits := strings.Split(versionText, ".")
	if len(splits) != 3 {
		return nil, fmt.Errorf("Incorrect Version format. Version should be in the form 1.5.7")
	}
	Major, err := strconv.Atoi(splits[0])
	if err != nil {
		return nil, VersionError("major", splits[0], err)
	}
	Minor, err := strconv.Atoi(splits[1])
	if err != nil {
		return nil, VersionError("minor", splits[1], err)
	}
	Patch, err := strconv.Atoi(splits[2])
	if err != nil {
		return nil, VersionError("patch", splits[2], err)
	}

	return &Version{Major, Minor, Patch}, nil
}

func VersionError(level, text string, err error) error {
	return fmt.Errorf("Error parsing %s Version %s to integer. %s", level, text, err.Error())
}

func (Version *Version) IsBetween(lower *Version, greater *Version) bool {
	return Version.IsGreaterThanEqualTo(lower) && Version.IsLesserThanEqualTo(greater)
}

func (Version *Version) IsLesserThan(version1 *Version) bool {
	return CompareVersions(Version, version1, LesserThanFunc)
}

func (Version *Version) IsGreaterThan(version1 *Version) bool {
	return CompareVersions(Version, version1, GreaterThanFunc)
}

func (Version *Version) IsLesserThanEqualTo(version1 *Version) bool {
	return Version.IsLesserThan(version1) || Version.IsEqualTo(version1)
}

func (Version *Version) IsGreaterThanEqualTo(version1 *Version) bool {
	return Version.IsGreaterThan(version1) || Version.IsEqualTo(version1)
}

func (Version *Version) IsEqualTo(version1 *Version) bool {
	return IsEqual(Version.Major, version1.Major) && IsEqual(Version.Minor, version1.Minor) && IsEqual(Version.Patch, version1.Patch)
}

func CompareVersions(first *Version, second *Version, compareFunc func(int, int) bool) bool {
	if compareFunc(first.Major, second.Major) {
		return true
	} else if IsEqual(first.Major, second.Major) {
		if compareFunc(first.Minor, second.Minor) {
			return true
		} else if IsEqual(first.Minor, second.Minor) {
			if compareFunc(first.Patch, second.Patch) {
				return true
			}
			return false
		}
	}
	return false
}

func LesserThanFunc(first, second int) bool {
	return first < second
}

func GreaterThanFunc(first, second int) bool {
	return first > second
}

func IsEqual(first, second int) bool {
	return first == second
}

func (Version *Version) String() string {
	return fmt.Sprintf("%d.%d.%d", Version.Major, Version.Minor, Version.Patch)
}

// FullVersion returns the CurrentGaugeVersion including build metadata.
func FullVersion() string {
	var metadata string
	if BuildMetadata != "" {
		metadata = fmt.Sprintf(".%s", BuildMetadata)
	}
	return fmt.Sprintf("%s%s", CurrentGaugeVersion.String(), metadata)
}

type byDecreasingVersion []*Version

func (a byDecreasingVersion) Len() int      { return len(a) }
func (a byDecreasingVersion) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a byDecreasingVersion) Less(i, j int) bool {
	return a[i].IsGreaterThan(a[j])
}

func GetLatestVersion(versions []*Version) *Version {
	sort.Sort(byDecreasingVersion(versions))
	return versions[0]
}

func CheckCompatibility(currentVersion *Version, versionSupport *VersionSupport) error {
	minSupportVersion, err := ParseVersion(versionSupport.Minimum)
	if err != nil {
		return fmt.Errorf("Invalid minimum support version %s. : %s. ", versionSupport.Minimum, err.Error())
	}
	if versionSupport.Maximum != "" {
		maxSupportVersion, err := ParseVersion(versionSupport.Maximum)
		if err != nil {
			return fmt.Errorf("Invalid maximum support version %s. : %s. ", versionSupport.Maximum, err.Error())
		}
		if currentVersion.IsBetween(minSupportVersion, maxSupportVersion) {
			return nil
		}
		return fmt.Errorf("Version %s is not between %s and %s", currentVersion, minSupportVersion, maxSupportVersion)
	}

	if minSupportVersion.IsLesserThanEqualTo(currentVersion) {
		return nil
	}
	return fmt.Errorf("Incompatible version. Minimum support version %s is higher than current version %s", minSupportVersion, currentVersion)
}
<commit_msg>bumped up version to 0.8.5<commit_after>// Copyright 2015 ThoughtWorks, Inc.

// This file is part of Gauge.

// Gauge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either Version 3 of the License, or
// (at your option) any later Version.

// Gauge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Gauge.  If not, see <http://www.gnu.org/licenses/>.

package version

import (
	"fmt"
	"sort"
	"strconv"
	"strings"
)

// CurrentGaugeVersion represents the current version of Gauge
var CurrentGaugeVersion = &Version{0, 8, 5}

// BuildMetadata represents build information of current release (e.g, nightly build information)
var BuildMetadata = ""

type Version struct {
	Major int
	Minor int
	Patch int
}

type VersionSupport struct {
	Minimum string
	Maximum string
}

func ParseVersion(versionText string) (*Version, error) {
	splits := strings.Split(versionText, ".")
	if len(splits) != 3 {
		return nil, fmt.Errorf("Incorrect Version format. Version should be in the form 1.5.7")
	}
	Major, err := strconv.Atoi(splits[0])
	if err != nil {
		return nil, VersionError("major", splits[0], err)
	}
	Minor, err := strconv.Atoi(splits[1])
	if err != nil {
		return nil, VersionError("minor", splits[1], err)
	}
	Patch, err := strconv.Atoi(splits[2])
	if err != nil {
		return nil, VersionError("patch", splits[2], err)
	}

	return &Version{Major, Minor, Patch}, nil
}

func VersionError(level, text string, err error) error {
	return fmt.Errorf("Error parsing %s Version %s to integer. %s", level, text, err.Error())
}

func (Version *Version) IsBetween(lower *Version, greater *Version) bool {
	return Version.IsGreaterThanEqualTo(lower) && Version.IsLesserThanEqualTo(greater)
}

func (Version *Version) IsLesserThan(version1 *Version) bool {
	return CompareVersions(Version, version1, LesserThanFunc)
}

func (Version *Version) IsGreaterThan(version1 *Version) bool {
	return CompareVersions(Version, version1, GreaterThanFunc)
}

func (Version *Version) IsLesserThanEqualTo(version1 *Version) bool {
	return Version.IsLesserThan(version1) || Version.IsEqualTo(version1)
}

func (Version *Version) IsGreaterThanEqualTo(version1 *Version) bool {
	return Version.IsGreaterThan(version1) || Version.IsEqualTo(version1)
}

func (Version *Version) IsEqualTo(version1 *Version) bool {
	return IsEqual(Version.Major, version1.Major) && IsEqual(Version.Minor, version1.Minor) && IsEqual(Version.Patch, version1.Patch)
}

func CompareVersions(first *Version, second *Version, compareFunc func(int, int) bool) bool {
	if compareFunc(first.Major, second.Major) {
		return true
	} else if IsEqual(first.Major, second.Major) {
		if compareFunc(first.Minor, second.Minor) {
			return true
		} else if IsEqual(first.Minor, second.Minor) {
			if compareFunc(first.Patch, second.Patch) {
				return true
			}
			return false
		}
	}
	return false
}

func LesserThanFunc(first, second int) bool {
	return first < second
}

func GreaterThanFunc(first, second int) bool {
	return first > second
}

func IsEqual(first, second int) bool {
	return first == second
}

func (Version *Version) String() string {
	return fmt.Sprintf("%d.%d.%d", Version.Major, Version.Minor, Version.Patch)
}

// FullVersion returns the CurrentGaugeVersion including build metadata.
func FullVersion() string {
	var metadata string
	if BuildMetadata != "" {
		metadata = fmt.Sprintf(".%s", BuildMetadata)
	}
	return fmt.Sprintf("%s%s", CurrentGaugeVersion.String(), metadata)
}

type byDecreasingVersion []*Version

func (a byDecreasingVersion) Len() int      { return len(a) }
func (a byDecreasingVersion) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a byDecreasingVersion) Less(i, j int) bool {
	return a[i].IsGreaterThan(a[j])
}

func GetLatestVersion(versions []*Version) *Version {
	sort.Sort(byDecreasingVersion(versions))
	return versions[0]
}

func CheckCompatibility(currentVersion *Version, versionSupport *VersionSupport) error {
	minSupportVersion, err := ParseVersion(versionSupport.Minimum)
	if err != nil {
		return fmt.Errorf("Invalid minimum support version %s. : %s. ", versionSupport.Minimum, err.Error())
	}
	if versionSupport.Maximum != "" {
		maxSupportVersion, err := ParseVersion(versionSupport.Maximum)
		if err != nil {
			return fmt.Errorf("Invalid maximum support version %s. : %s. ", versionSupport.Maximum, err.Error())
		}
		if currentVersion.IsBetween(minSupportVersion, maxSupportVersion) {
			return nil
		}
		return fmt.Errorf("Version %s is not between %s and %s", currentVersion, minSupportVersion, maxSupportVersion)
	}

	if minSupportVersion.IsLesserThanEqualTo(currentVersion) {
		return nil
	}
	return fmt.Errorf("Incompatible version. Minimum support version %s is higher than current version %s", minSupportVersion, currentVersion)
}
<|endoftext|>


<commit_before>package version

import (
	"bytes"
	"fmt"
)

// The git commit that was compiled. This will be filled in by the compiler.
var GitCommit string

// The main version number that is being run at the moment.
const Version = "0.11.0"

// A pre-release marker for the version. If this is "" (empty string)
// then it means that it is a final release. Otherwise, this is a pre-release
// such as "dev" (in development), "beta", "rc1", etc.
const VersionPrerelease = "dev"

func FormattedVersion() string {
	var versionString bytes.Buffer
	fmt.Fprintf(&versionString, "%s", Version)
	if VersionPrerelease != "" {
		fmt.Fprintf(&versionString, ".%s", VersionPrerelease)

		if GitCommit != "" {
			fmt.Fprintf(&versionString, " (%s)", GitCommit)
		}
	}

	return versionString.String()
}
<commit_msg>prepare for 0.11.0<commit_after>package version

import (
	"bytes"
	"fmt"
)

// The git commit that was compiled. This will be filled in by the compiler.
var GitCommit string

// The main version number that is being run at the moment.
const Version = "0.11.0"

// A pre-release marker for the version. If this is "" (empty string)
// then it means that it is a final release. Otherwise, this is a pre-release
// such as "dev" (in development), "beta", "rc1", etc.
const VersionPrerelease = ""

func FormattedVersion() string {
	var versionString bytes.Buffer
	fmt.Fprintf(&versionString, "%s", Version)
	if VersionPrerelease != "" {
		fmt.Fprintf(&versionString, ".%s", VersionPrerelease)

		if GitCommit != "" {
			fmt.Fprintf(&versionString, " (%s)", GitCommit)
		}
	}

	return versionString.String()
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Knative 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 version

import (
	"fmt"
	"os"
	"strings"

	"github.com/blang/semver/v4"
	"k8s.io/client-go/discovery"
)

const (
	// KubernetesMinVersionKey is the environment variable that can be used to override
	// the Kubernetes minimum version required by Knative.
	KubernetesMinVersionKey = "KUBERNETES_MIN_VERSION"

	// NOTE: If you are changing this line, please also update the minimum kubernetes
	// version listed here:
	// https://github.com/knative/docs/blob/mkdocs/docs/snippets/prerequisites.md
	defaultMinimumVersion = "v1.19.0"
)

func getMinimumVersion() string {
	if v := os.Getenv(KubernetesMinVersionKey); v != "" {
		return v
	}
	return defaultMinimumVersion
}

// CheckMinimumVersion checks if the currently installed version of
// Kubernetes is compatible with the minimum version required.
// Returns an error if its not.
//
// A Kubernetes discovery client can be passed in as the versioner
// like `CheckMinimumVersion(kubeClient.Discovery())`.
func CheckMinimumVersion(versioner discovery.ServerVersionInterface) error {
	v, err := versioner.ServerVersion()
	if err != nil {
		return err
	}

	currentVersion, err := semver.Make(normalizeVersion(v.GitVersion))
	if err != nil {
		return err
	}
	minimumVersion, err := semver.Make(normalizeVersion(getMinimumVersion()))
	if err != nil {
		return err
	}

	// If no specific pre-release requirement is set, we default to "-0" to always allow
	// pre-release versions of the same Major.Minor.Patch version.
	if len(minimumVersion.Pre) == 0 {
		minimumVersion.Pre = []semver.PRVersion{{VersionNum: 0}}
	}

	// Compare returns 1 if the first version is greater than the
	// second version.
	if currentVersion.LT(minimumVersion) {
		if len(currentVersion.Pre) > 0 {
			return fmt.Errorf("pre-release kubernetes version %q is not compatible, need at least %q (this can be overridden with the env var %q); note pre-release version is smaller than the corresponding release version (e.g. 1.x.y-z < 1.x.y), using 1.x.y-0 as the minimum version is likely to help in this case",
				currentVersion, minimumVersion, KubernetesMinVersionKey)
		}
		return fmt.Errorf("kubernetes version %q is not compatible, need at least %q (this can be overridden with the env var %q)",
			currentVersion, minimumVersion, KubernetesMinVersionKey)
	}
	return nil
}

func normalizeVersion(v string) string {
	if strings.HasPrefix(v, "v") {
		// No need to account for unicode widths.
		return v[1:]
	}
	return v
}
<commit_msg>Update version check comment (#2234)<commit_after>/*
Copyright 2019 The Knative 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 version

import (
	"fmt"
	"os"
	"strings"

	"github.com/blang/semver/v4"
	"k8s.io/client-go/discovery"
)

const (
	// KubernetesMinVersionKey is the environment variable that can be used to override
	// the Kubernetes minimum version required by Knative.
	KubernetesMinVersionKey = "KUBERNETES_MIN_VERSION"

	// NOTE: If you are changing this line, please also update the minimum kubernetes
	// version listed here:
	// https://github.com/knative/docs/blob/mkdocs/docs/snippets/prerequisites.md
	defaultMinimumVersion = "v1.19.0"
)

func getMinimumVersion() string {
	if v := os.Getenv(KubernetesMinVersionKey); v != "" {
		return v
	}
	return defaultMinimumVersion
}

// CheckMinimumVersion checks if the currently installed version of
// Kubernetes is compatible with the minimum version required.
// Returns an error if its not.
//
// A Kubernetes discovery client can be passed in as the versioner
// like `CheckMinimumVersion(kubeClient.Discovery())`.
func CheckMinimumVersion(versioner discovery.ServerVersionInterface) error {
	v, err := versioner.ServerVersion()
	if err != nil {
		return err
	}

	currentVersion, err := semver.Make(normalizeVersion(v.GitVersion))
	if err != nil {
		return err
	}
	minimumVersion, err := semver.Make(normalizeVersion(getMinimumVersion()))
	if err != nil {
		return err
	}

	// If no specific pre-release requirement is set, we default to "-0" to always allow
	// pre-release versions of the same Major.Minor.Patch version.
	if len(minimumVersion.Pre) == 0 {
		minimumVersion.Pre = []semver.PRVersion{{VersionNum: 0}}
	}

	// Return error if the current version is less than the minimum version required.
	if currentVersion.LT(minimumVersion) {
		if len(currentVersion.Pre) > 0 {
			return fmt.Errorf("pre-release kubernetes version %q is not compatible, need at least %q (this can be overridden with the env var %q); note pre-release version is smaller than the corresponding release version (e.g. 1.x.y-z < 1.x.y), using 1.x.y-0 as the minimum version is likely to help in this case",
				currentVersion, minimumVersion, KubernetesMinVersionKey)
		}
		return fmt.Errorf("kubernetes version %q is not compatible, need at least %q (this can be overridden with the env var %q)",
			currentVersion, minimumVersion, KubernetesMinVersionKey)
	}
	return nil
}

func normalizeVersion(v string) string {
	if strings.HasPrefix(v, "v") {
		// No need to account for unicode widths.
		return v[1:]
	}
	return v
}
<|endoftext|>


<commit_before>// Copyright 2014 CoreOS, Inc.
//
// 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 version

const Version = "0.3.0+git"
<commit_msg>version: bump to v0.3.1<commit_after>// Copyright 2014 CoreOS, Inc.
//
// 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 version

const Version = "0.3.1"
<|endoftext|>


<commit_before>package version

const Maj = "0"
const Min = "11"
const Fix = "0"

var (
	// The full version string
	Version = "0.11.0"

	// GitCommit is set with --ldflags "-X main.gitCommit=$(git rev-parse HEAD)"
	GitCommit string
)

func init() {
	if GitCommit != "" {
		Version += "-" + GitCommit[:8]
	}
}
<commit_msg>bump version to 0.11.1<commit_after>package version

const Maj = "0"
const Min = "11"
const Fix = "0"

var (
	// The full version string
	Version = "0.11.1"

	// GitCommit is set with --ldflags "-X main.gitCommit=$(git rev-parse HEAD)"
	GitCommit string
)

func init() {
	if GitCommit != "" {
		Version += "-" + GitCommit[:8]
	}
}
<|endoftext|>


<commit_before>package token

import (
	"fmt"
	"github.com/dgrijalva/jwt-go"
	"github.com/yang-f/beauty/settings"
	"time"
)

func Generate(key string) (string, error) {
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
		"key": key,
		"exp": (time.Now().Add(time.Minute * 60 * 24 * 2)).Unix(),
	})

	tokenString, err := token.SignedString(settings.HmacSampleSecret)
	// fmt.Println(tokenString, err)
	return tokenString, err
}

func Valid(tokenString string) (string, error) {
	token1, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
		}
		return settings.HmacSampleSecret, nil
	})

	if claims, ok := token1.Claims.(jwt.MapClaims); ok && token1.Valid {
		return fmt.Sprintf("%v", claims["key"]), nil
	} else {
		return "", err
	}
}
<commit_msg>sessions<commit_after>package token

import (
	"fmt"
	"github.com/dgrijalva/jwt-go"
	"github.com/yang-f/beauty/settings"
	"time"
)

func Generate(key string) (string, error) {
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
		"key": key,
		"exp": (time.Now().Add(time.Minute * 60 * 24 * 2)).Unix(),
	})

	tokenString, err := token.SignedString(settings.HmacSampleSecret)
	return tokenString, err
}

func Valid(tokenString string) (string, error) {
	token1, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
		}
		return settings.HmacSampleSecret, nil
	})

	if claims, ok := token1.Claims.(jwt.MapClaims); ok && token1.Valid {
		return fmt.Sprintf("%v", claims["key"]), nil
	} else {
		return "", err
	}
}
<|endoftext|>