<commit_before>package ssh

import (
	"fmt"
	"strings"

	"github.com/hashicorp/vault/helper/cidrutil"
	"github.com/hashicorp/vault/logical"
	"github.com/hashicorp/vault/logical/framework"
)

const (
	KeyTypeOTP     = "otp"
	KeyTypeDynamic = "dynamic"
)

// Structure that represents a role in SSH backend. This is a common role structure
// for both OTP and Dynamic roles. Not all the fields are mandatory for both type.
// Some are applicable for one and not for other. It doesn't matter.
type sshRole struct {
	KeyType         string `mapstructure:"key_type" json:"key_type"`
	KeyName         string `mapstructure:"key" json:"key"`
	KeyBits         int    `mapstructure:"key_bits" json:"key_bits"`
	AdminUser       string `mapstructure:"admin_user" json:"admin_user"`
	DefaultUser     string `mapstructure:"default_user" json:"default_user"`
	CIDRList        string `mapstructure:"cidr_list" json:"cidr_list"`
	ExcludeCIDRList string `mapstructure:"exclude_cidr_list" json:"exclude_cidr_list"`
	Port            int    `mapstructure:"port" json:"port"`
	InstallScript   string `mapstructure:"install_script" json:"install_script"`
	AllowedUsers    string `mapstructure:"allowed_users" json:"allowed_users"`
	KeyOptionSpecs  string `mapstructure:"key_option_specs" json:"key_option_specs"`
}

func pathListRoles(b *backend) *framework.Path {
	return &framework.Path{
		Pattern: "roles/?$",

		Callbacks: map[logical.Operation]framework.OperationFunc{
			logical.ListOperation: b.pathRoleList,
		},

		HelpSynopsis:    pathRoleHelpSyn,
		HelpDescription: pathRoleHelpDesc,
	}
}

func pathRoles(b *backend) *framework.Path {
	return &framework.Path{
		Pattern: "roles/" + framework.GenericNameRegex("role"),
		Fields: map[string]*framework.FieldSchema{
			"role": &framework.FieldSchema{
				Type: framework.TypeString,
				Description: `
				[Required for both types]
				Name of the role being created.`,
			},
			"key": &framework.FieldSchema{
				Type: framework.TypeString,
				Description: `
				[Required for Dynamic type] [Not applicable for OTP type]
				Name of the registered key in Vault. Before creating the role, use the
				'keys/' endpoint to create a named key.`,
			},
			"admin_user": &framework.FieldSchema{
				Type: framework.TypeString,
				Description: `
				[Required for Dynamic type] [Not applicable for OTP type]
				Admin user at remote host. The shared key being registered should be
				for this user and should have root privileges. Everytime a dynamic
				credential is being generated for other users, Vault uses this admin
				username to login to remote host and install the generated credential
				for the other user.`,
			},
			"default_user": &framework.FieldSchema{
				Type: framework.TypeString,
				Description: `
				[Required for both types]
				Default username for which a credential will be generated.
				When the endpoint 'creds/' is used without a username, this
				value will be used as default username.`,
			},
			"cidr_list": &framework.FieldSchema{
				Type: framework.TypeString,
				Description: `
				[Optional for both types]
				Comma separated list of CIDR blocks for which the role is applicable for.
				CIDR blocks can belong to more than one role.`,
			},
			"exclude_cidr_list": &framework.FieldSchema{
				Type: framework.TypeString,
				Description: `
				[Optional for both types]
				Comma separated list of CIDR blocks. IP addresses belonging to these blocks are not
				accepted by the role. This is particularly useful when big CIDR blocks are being used
				by the role and certain parts of it needs to be kept out.`,
			},
			"port": &framework.FieldSchema{
				Type: framework.TypeInt,
				Description: `
				[Optional for both types]
				Port number for SSH connection. Default is '22'. Port number does not
				play any role in creation of OTP. For 'otp' type, this is just a way
				to inform client about the port number to use. Port number will be
				returned to client by Vault server along with OTP.`,
			},
			"key_type": &framework.FieldSchema{
				Type: framework.TypeString,
				Description: `
				[Required for both types]
				Type of key used to login to hosts. It can be either 'otp' or 'dynamic'.
				'otp' type requires agent to be installed in remote hosts.`,
			},
			"key_bits": &framework.FieldSchema{
				Type: framework.TypeInt,
				Description: `
				[Optional for Dynamic type] [Not applicable for OTP type]
				Length of the RSA dynamic key in bits. It is 1024 by default or it can be 2048.`,
			},
			"install_script": &framework.FieldSchema{
				Type: framework.TypeString,
				Description: `
				[Optional for Dynamic type] [Not-applicable for OTP type]
				Script used to install and uninstall public keys in the target machine.
				The inbuilt default install script will be for Linux hosts. For sample
				script, refer the project documentation website.`,
			},
			"allowed_users": &framework.FieldSchema{
				Type: framework.TypeString,
				Description: `
				[Optional for both types]
				If this option is not specified, client can request for a credential for
				any valid user at the remote host, including the admin user. If only certain
				usernames are to be allowed, then this list enforces it. If this field is
				set, then credentials can only be created for default_user and usernames
				present in this list.
				`,
			},
			"key_option_specs": &framework.FieldSchema{
				Type: framework.TypeString,
				Description: `
				[Optional for Dynamic type] [Not applicable for OTP type]
				Comma separated option specifications which will be prefixed to RSA key in
				authorized_keys file. Options should be valid and comply with authorized_keys
				file format and should not contain spaces.
				`,
			},
		},

		Callbacks: map[logical.Operation]framework.OperationFunc{
			logical.ReadOperation:   b.pathRoleRead,
			logical.UpdateOperation: b.pathRoleWrite,
			logical.DeleteOperation: b.pathRoleDelete,
		},

		HelpSynopsis:    pathRoleHelpSyn,
		HelpDescription: pathRoleHelpDesc,
	}
}

func (b *backend) pathRoleWrite(req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
	roleName := d.Get("role").(string)
	if roleName == "" {
		return logical.ErrorResponse("missing role name"), nil
	}

	// Allowed users is an optional field, applicable for both OTP and Dynamic types.
	allowedUsers := d.Get("allowed_users").(string)

	defaultUser := d.Get("default_user").(string)
	if defaultUser == "" {
		return logical.ErrorResponse("missing default user"), nil
	}

	// Validate the CIDR blocks
	cidrList := d.Get("cidr_list").(string)
	valid, err := cidrutil.ValidateCIDRListString(cidrList, ",")
	if err != nil {
		return nil, fmt.Errorf("failed to validate cidr_list: %v", err)
	}
	if !valid {
		return logical.ErrorResponse("failed to validate cidr_list"), nil
	}

	// Validate the excluded CIDR blocks
	excludeCidrList := d.Get("exclude_cidr_list").(string)
	valid, err = cidrutil.ValidateCIDRListString(excludeCidrList, ",")
	if err != nil {
		return nil, fmt.Errorf("failed to validate exclude_cidr_list entry: %v", err)
	}
	if !valid {
		return logical.ErrorResponse(fmt.Sprintf("failed to validate exclude_cidr_list entry: %v", err)), nil
	}

	port := d.Get("port").(int)
	if port == 0 {
		port = 22
	}

	keyType := d.Get("key_type").(string)
	if keyType == "" {
		return logical.ErrorResponse("missing key type"), nil
	}
	keyType = strings.ToLower(keyType)

	var roleEntry sshRole
	if keyType == KeyTypeOTP {
		// Admin user is not used if OTP key type is used because there is
		// no need to login to remote machine.
		adminUser := d.Get("admin_user").(string)
		if adminUser != "" {
			return logical.ErrorResponse("admin user not required for OTP type"), nil
		}

		// Below are the only fields used from the role structure for OTP type.
		roleEntry = sshRole{
			DefaultUser:     defaultUser,
			CIDRList:        cidrList,
			ExcludeCIDRList: excludeCidrList,
			KeyType:         KeyTypeOTP,
			Port:            port,
			AllowedUsers:    allowedUsers,
		}
	} else if keyType == KeyTypeDynamic {
		// Key name is required by dynamic type and not by OTP type.
		keyName := d.Get("key").(string)
		if keyName == "" {
			return logical.ErrorResponse("missing key name"), nil
		}
		keyEntry, err := req.Storage.Get(fmt.Sprintf("keys/%s", keyName))
		if err != nil || keyEntry == nil {
			return logical.ErrorResponse(fmt.Sprintf("invalid 'key': '%s'", keyName)), nil
		}

		installScript := d.Get("install_script").(string)
		keyOptionSpecs := d.Get("key_option_specs").(string)

		// Setting the default script here. The script will install the
		// generated public key in the authorized_keys file of linux host.
		if installScript == "" {
			installScript = DefaultPublicKeyInstallScript
		}

		adminUser := d.Get("admin_user").(string)
		if adminUser == "" {
			return logical.ErrorResponse("missing admin username"), nil
		}

		// This defaults to 1024 and it can also be 2048.
		keyBits := d.Get("key_bits").(int)
		if keyBits != 0 && keyBits != 1024 && keyBits != 2048 {
			return logical.ErrorResponse("invalid key_bits field"), nil
		}

		// If user has not set this field, default it to 1024
		if keyBits == 0 {
			keyBits = 1024
		}

		// Store all the fields required by dynamic key type
		roleEntry = sshRole{
			KeyName:         keyName,
			AdminUser:       adminUser,
			DefaultUser:     defaultUser,
			CIDRList:        cidrList,
			ExcludeCIDRList: excludeCidrList,
			Port:            port,
			KeyType:         KeyTypeDynamic,
			KeyBits:         keyBits,
			InstallScript:   installScript,
			AllowedUsers:    allowedUsers,
			KeyOptionSpecs:  keyOptionSpecs,
		}
	} else {
		return logical.ErrorResponse("invalid key type"), nil
	}

	entry, err := logical.StorageEntryJSON(fmt.Sprintf("roles/%s", roleName), roleEntry)
	if err != nil {
		return nil, err
	}

	if err := req.Storage.Put(entry); err != nil {
		return nil, err
	}
	return nil, nil
}

func (b *backend) getRole(s logical.Storage, n string) (*sshRole, error) {
	entry, err := s.Get("roles/" + n)
	if err != nil {
		return nil, err
	}
	if entry == nil {
		return nil, nil
	}

	var result sshRole
	if err := entry.DecodeJSON(&result); err != nil {
		return nil, err
	}

	return &result, nil
}

func (b *backend) pathRoleList(req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
	entries, err := req.Storage.List("roles/")
	if err != nil {
		return nil, err
	}

	return logical.ListResponse(entries), nil
}

func (b *backend) pathRoleRead(req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
	role, err := b.getRole(req.Storage, d.Get("role").(string))
	if err != nil {
		return nil, err
	}
	if role == nil {
		return nil, nil
	}

	// Return information should be based on the key type of the role
	if role.KeyType == KeyTypeOTP {
		return &logical.Response{
			Data: map[string]interface{}{
				"default_user":      role.DefaultUser,
				"cidr_list":         role.CIDRList,
				"exclude_cidr_list": role.ExcludeCIDRList,
				"key_type":          role.KeyType,
				"port":              role.Port,
				"allowed_users":     role.AllowedUsers,
			},
		}, nil
	} else {
		return &logical.Response{
			Data: map[string]interface{}{
				"key":               role.KeyName,
				"admin_user":        role.AdminUser,
				"default_user":      role.DefaultUser,
				"cidr_list":         role.CIDRList,
				"exclude_cidr_list": role.ExcludeCIDRList,
				"port":              role.Port,
				"key_type":          role.KeyType,
				"key_bits":          role.KeyBits,
				"allowed_users":     role.AllowedUsers,
				"key_option_specs":  role.KeyOptionSpecs,
				// Returning install script will make the output look messy.
				// But this is one way for clients to see the script that is
				// being used to install the key. If there is some problem,
				// the script can be modified and configured by clients.
				"install_script": role.InstallScript,
			},
		}, nil
	}
}

func (b *backend) pathRoleDelete(req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
	roleName := d.Get("role").(string)

	// If the role was given privilege to accept any IP address, there will
	// be an entry for this role in zero-address roles list. Before the role
	// is removed, the entry in the list has to be removed.
	err := b.removeZeroAddressRole(req.Storage, roleName)
	if err != nil {
		return nil, err
	}

	err = req.Storage.Delete(fmt.Sprintf("roles/%s", roleName))
	if err != nil {
		return nil, err
	}
	return nil, nil
}

const pathRoleHelpSyn = `
Manage the 'roles' that can be created with this backend.
`

const pathRoleHelpDesc = `
This path allows you to manage the roles that are used to generate credentials.

Role takes a 'key_type' parameter that decides what type of credential this role
can generate. If remote hosts have Vault SSH Agent installed, an 'otp' type can
be used, otherwise 'dynamic' type can be used.

If the backend is mounted at "ssh" and the role is created at "ssh/roles/web",
then a user could request for a credential at "ssh/creds/web" for an IP that
belongs to the role. The credential will be for the 'default_user' registered
with the role. There is also an optional parameter 'username' for 'creds/' endpoint.
`
<commit_msg>Fix ssh tests<commit_after>package ssh

import (
	"fmt"
	"strings"

	"github.com/hashicorp/vault/helper/cidrutil"
	"github.com/hashicorp/vault/logical"
	"github.com/hashicorp/vault/logical/framework"
)

const (
	KeyTypeOTP     = "otp"
	KeyTypeDynamic = "dynamic"
)

// Structure that represents a role in SSH backend. This is a common role structure
// for both OTP and Dynamic roles. Not all the fields are mandatory for both type.
// Some are applicable for one and not for other. It doesn't matter.
type sshRole struct {
	KeyType         string `mapstructure:"key_type" json:"key_type"`
	KeyName         string `mapstructure:"key" json:"key"`
	KeyBits         int    `mapstructure:"key_bits" json:"key_bits"`
	AdminUser       string `mapstructure:"admin_user" json:"admin_user"`
	DefaultUser     string `mapstructure:"default_user" json:"default_user"`
	CIDRList        string `mapstructure:"cidr_list" json:"cidr_list"`
	ExcludeCIDRList string `mapstructure:"exclude_cidr_list" json:"exclude_cidr_list"`
	Port            int    `mapstructure:"port" json:"port"`
	InstallScript   string `mapstructure:"install_script" json:"install_script"`
	AllowedUsers    string `mapstructure:"allowed_users" json:"allowed_users"`
	KeyOptionSpecs  string `mapstructure:"key_option_specs" json:"key_option_specs"`
}

func pathListRoles(b *backend) *framework.Path {
	return &framework.Path{
		Pattern: "roles/?$",

		Callbacks: map[logical.Operation]framework.OperationFunc{
			logical.ListOperation: b.pathRoleList,
		},

		HelpSynopsis:    pathRoleHelpSyn,
		HelpDescription: pathRoleHelpDesc,
	}
}

func pathRoles(b *backend) *framework.Path {
	return &framework.Path{
		Pattern: "roles/" + framework.GenericNameRegex("role"),
		Fields: map[string]*framework.FieldSchema{
			"role": &framework.FieldSchema{
				Type: framework.TypeString,
				Description: `
				[Required for both types]
				Name of the role being created.`,
			},
			"key": &framework.FieldSchema{
				Type: framework.TypeString,
				Description: `
				[Required for Dynamic type] [Not applicable for OTP type]
				Name of the registered key in Vault. Before creating the role, use the
				'keys/' endpoint to create a named key.`,
			},
			"admin_user": &framework.FieldSchema{
				Type: framework.TypeString,
				Description: `
				[Required for Dynamic type] [Not applicable for OTP type]
				Admin user at remote host. The shared key being registered should be
				for this user and should have root privileges. Everytime a dynamic
				credential is being generated for other users, Vault uses this admin
				username to login to remote host and install the generated credential
				for the other user.`,
			},
			"default_user": &framework.FieldSchema{
				Type: framework.TypeString,
				Description: `
				[Required for both types]
				Default username for which a credential will be generated.
				When the endpoint 'creds/' is used without a username, this
				value will be used as default username.`,
			},
			"cidr_list": &framework.FieldSchema{
				Type: framework.TypeString,
				Description: `
				[Optional for both types]
				Comma separated list of CIDR blocks for which the role is applicable for.
				CIDR blocks can belong to more than one role.`,
			},
			"exclude_cidr_list": &framework.FieldSchema{
				Type: framework.TypeString,
				Description: `
				[Optional for both types]
				Comma separated list of CIDR blocks. IP addresses belonging to these blocks are not
				accepted by the role. This is particularly useful when big CIDR blocks are being used
				by the role and certain parts of it needs to be kept out.`,
			},
			"port": &framework.FieldSchema{
				Type: framework.TypeInt,
				Description: `
				[Optional for both types]
				Port number for SSH connection. Default is '22'. Port number does not
				play any role in creation of OTP. For 'otp' type, this is just a way
				to inform client about the port number to use. Port number will be
				returned to client by Vault server along with OTP.`,
			},
			"key_type": &framework.FieldSchema{
				Type: framework.TypeString,
				Description: `
				[Required for both types]
				Type of key used to login to hosts. It can be either 'otp' or 'dynamic'.
				'otp' type requires agent to be installed in remote hosts.`,
			},
			"key_bits": &framework.FieldSchema{
				Type: framework.TypeInt,
				Description: `
				[Optional for Dynamic type] [Not applicable for OTP type]
				Length of the RSA dynamic key in bits. It is 1024 by default or it can be 2048.`,
			},
			"install_script": &framework.FieldSchema{
				Type: framework.TypeString,
				Description: `
				[Optional for Dynamic type] [Not-applicable for OTP type]
				Script used to install and uninstall public keys in the target machine.
				The inbuilt default install script will be for Linux hosts. For sample
				script, refer the project documentation website.`,
			},
			"allowed_users": &framework.FieldSchema{
				Type: framework.TypeString,
				Description: `
				[Optional for both types]
				If this option is not specified, client can request for a credential for
				any valid user at the remote host, including the admin user. If only certain
				usernames are to be allowed, then this list enforces it. If this field is
				set, then credentials can only be created for default_user and usernames
				present in this list.
				`,
			},
			"key_option_specs": &framework.FieldSchema{
				Type: framework.TypeString,
				Description: `
				[Optional for Dynamic type] [Not applicable for OTP type]
				Comma separated option specifications which will be prefixed to RSA key in
				authorized_keys file. Options should be valid and comply with authorized_keys
				file format and should not contain spaces.
				`,
			},
		},

		Callbacks: map[logical.Operation]framework.OperationFunc{
			logical.ReadOperation:   b.pathRoleRead,
			logical.UpdateOperation: b.pathRoleWrite,
			logical.DeleteOperation: b.pathRoleDelete,
		},

		HelpSynopsis:    pathRoleHelpSyn,
		HelpDescription: pathRoleHelpDesc,
	}
}

func (b *backend) pathRoleWrite(req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
	roleName := d.Get("role").(string)
	if roleName == "" {
		return logical.ErrorResponse("missing role name"), nil
	}

	// Allowed users is an optional field, applicable for both OTP and Dynamic types.
	allowedUsers := d.Get("allowed_users").(string)

	defaultUser := d.Get("default_user").(string)
	if defaultUser == "" {
		return logical.ErrorResponse("missing default user"), nil
	}

	// Validate the CIDR blocks
	cidrList := d.Get("cidr_list").(string)
	if cidrList != "" {
		valid, err := cidrutil.ValidateCIDRListString(cidrList, ",")
		if err != nil {
			return nil, fmt.Errorf("failed to validate cidr_list: %v", err)
		}
		if !valid {
			return logical.ErrorResponse("failed to validate cidr_list"), nil
		}
	}

	// Validate the excluded CIDR blocks
	excludeCidrList := d.Get("exclude_cidr_list").(string)
	if excludeCidrList != "" {
		valid, err := cidrutil.ValidateCIDRListString(excludeCidrList, ",")
		if err != nil {
			return nil, fmt.Errorf("failed to validate exclude_cidr_list entry: %v", err)
		}
		if !valid {
			return logical.ErrorResponse(fmt.Sprintf("failed to validate exclude_cidr_list entry: %v", err)), nil
		}
	}

	port := d.Get("port").(int)
	if port == 0 {
		port = 22
	}

	keyType := d.Get("key_type").(string)
	if keyType == "" {
		return logical.ErrorResponse("missing key type"), nil
	}
	keyType = strings.ToLower(keyType)

	var roleEntry sshRole
	if keyType == KeyTypeOTP {
		// Admin user is not used if OTP key type is used because there is
		// no need to login to remote machine.
		adminUser := d.Get("admin_user").(string)
		if adminUser != "" {
			return logical.ErrorResponse("admin user not required for OTP type"), nil
		}

		// Below are the only fields used from the role structure for OTP type.
		roleEntry = sshRole{
			DefaultUser:     defaultUser,
			CIDRList:        cidrList,
			ExcludeCIDRList: excludeCidrList,
			KeyType:         KeyTypeOTP,
			Port:            port,
			AllowedUsers:    allowedUsers,
		}
	} else if keyType == KeyTypeDynamic {
		// Key name is required by dynamic type and not by OTP type.
		keyName := d.Get("key").(string)
		if keyName == "" {
			return logical.ErrorResponse("missing key name"), nil
		}
		keyEntry, err := req.Storage.Get(fmt.Sprintf("keys/%s", keyName))
		if err != nil || keyEntry == nil {
			return logical.ErrorResponse(fmt.Sprintf("invalid 'key': '%s'", keyName)), nil
		}

		installScript := d.Get("install_script").(string)
		keyOptionSpecs := d.Get("key_option_specs").(string)

		// Setting the default script here. The script will install the
		// generated public key in the authorized_keys file of linux host.
		if installScript == "" {
			installScript = DefaultPublicKeyInstallScript
		}

		adminUser := d.Get("admin_user").(string)
		if adminUser == "" {
			return logical.ErrorResponse("missing admin username"), nil
		}

		// This defaults to 1024 and it can also be 2048.
		keyBits := d.Get("key_bits").(int)
		if keyBits != 0 && keyBits != 1024 && keyBits != 2048 {
			return logical.ErrorResponse("invalid key_bits field"), nil
		}

		// If user has not set this field, default it to 1024
		if keyBits == 0 {
			keyBits = 1024
		}

		// Store all the fields required by dynamic key type
		roleEntry = sshRole{
			KeyName:         keyName,
			AdminUser:       adminUser,
			DefaultUser:     defaultUser,
			CIDRList:        cidrList,
			ExcludeCIDRList: excludeCidrList,
			Port:            port,
			KeyType:         KeyTypeDynamic,
			KeyBits:         keyBits,
			InstallScript:   installScript,
			AllowedUsers:    allowedUsers,
			KeyOptionSpecs:  keyOptionSpecs,
		}
	} else {
		return logical.ErrorResponse("invalid key type"), nil
	}

	entry, err := logical.StorageEntryJSON(fmt.Sprintf("roles/%s", roleName), roleEntry)
	if err != nil {
		return nil, err
	}

	if err := req.Storage.Put(entry); err != nil {
		return nil, err
	}
	return nil, nil
}

func (b *backend) getRole(s logical.Storage, n string) (*sshRole, error) {
	entry, err := s.Get("roles/" + n)
	if err != nil {
		return nil, err
	}
	if entry == nil {
		return nil, nil
	}

	var result sshRole
	if err := entry.DecodeJSON(&result); err != nil {
		return nil, err
	}

	return &result, nil
}

func (b *backend) pathRoleList(req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
	entries, err := req.Storage.List("roles/")
	if err != nil {
		return nil, err
	}

	return logical.ListResponse(entries), nil
}

func (b *backend) pathRoleRead(req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
	role, err := b.getRole(req.Storage, d.Get("role").(string))
	if err != nil {
		return nil, err
	}
	if role == nil {
		return nil, nil
	}

	// Return information should be based on the key type of the role
	if role.KeyType == KeyTypeOTP {
		return &logical.Response{
			Data: map[string]interface{}{
				"default_user":      role.DefaultUser,
				"cidr_list":         role.CIDRList,
				"exclude_cidr_list": role.ExcludeCIDRList,
				"key_type":          role.KeyType,
				"port":              role.Port,
				"allowed_users":     role.AllowedUsers,
			},
		}, nil
	} else {
		return &logical.Response{
			Data: map[string]interface{}{
				"key":               role.KeyName,
				"admin_user":        role.AdminUser,
				"default_user":      role.DefaultUser,
				"cidr_list":         role.CIDRList,
				"exclude_cidr_list": role.ExcludeCIDRList,
				"port":              role.Port,
				"key_type":          role.KeyType,
				"key_bits":          role.KeyBits,
				"allowed_users":     role.AllowedUsers,
				"key_option_specs":  role.KeyOptionSpecs,
				// Returning install script will make the output look messy.
				// But this is one way for clients to see the script that is
				// being used to install the key. If there is some problem,
				// the script can be modified and configured by clients.
				"install_script": role.InstallScript,
			},
		}, nil
	}
}

func (b *backend) pathRoleDelete(req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
	roleName := d.Get("role").(string)

	// If the role was given privilege to accept any IP address, there will
	// be an entry for this role in zero-address roles list. Before the role
	// is removed, the entry in the list has to be removed.
	err := b.removeZeroAddressRole(req.Storage, roleName)
	if err != nil {
		return nil, err
	}

	err = req.Storage.Delete(fmt.Sprintf("roles/%s", roleName))
	if err != nil {
		return nil, err
	}
	return nil, nil
}

const pathRoleHelpSyn = `
Manage the 'roles' that can be created with this backend.
`

const pathRoleHelpDesc = `
This path allows you to manage the roles that are used to generate credentials.

Role takes a 'key_type' parameter that decides what type of credential this role
can generate. If remote hosts have Vault SSH Agent installed, an 'otp' type can
be used, otherwise 'dynamic' type can be used.

If the backend is mounted at "ssh" and the role is created at "ssh/roles/web",
then a user could request for a credential at "ssh/creds/web" for an IP that
belongs to the role. The credential will be for the 'default_user' registered
with the role. There is also an optional parameter 'username' for 'creds/' endpoint.
`
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"sync"

	"github.com/docker/docker/daemon/logger"
	"github.com/go-kit/kit/log"
	"github.com/go-kit/kit/log/level"
	"github.com/pkg/errors"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/common/model"

	"github.com/grafana/loki/clients/pkg/logentry/stages"
	"github.com/grafana/loki/clients/pkg/promtail/api"
	"github.com/grafana/loki/clients/pkg/promtail/client"

	"github.com/grafana/loki/pkg/logproto"
)

var jobName = "docker"

type loki struct {
	client  client.Client
	handler api.EntryHandler
	labels  model.LabelSet
	logger  log.Logger

	closed bool
	mutex  sync.RWMutex

	stop func()
}

// New create a new Loki logger that forward logs to Loki instance
func New(logCtx logger.Info, logger log.Logger) (logger.Logger, error) {
	logger = log.With(logger, "container_id", logCtx.ContainerID)
	cfg, err := parseConfig(logCtx)
	if err != nil {
		return nil, err
	}
	c, err := client.New(prometheus.DefaultRegisterer, cfg.clientConfig, logger)
	if err != nil {
		return nil, err
	}
	var handler api.EntryHandler = c
	var stop = func() {}
	if len(cfg.pipeline.PipelineStages) != 0 {
		pipeline, err := stages.NewPipeline(logger, cfg.pipeline.PipelineStages, &jobName, prometheus.DefaultRegisterer)
		if err != nil {
			return nil, err
		}
		handler = pipeline.Wrap(c)
		stop = handler.Stop
	}
	return &loki{
		client:  c,
		labels:  cfg.labels,
		logger:  logger,
		handler: handler,
		stop:    stop,
	}, nil
}

// Log implements `logger.Logger`
func (l *loki) Log(m *logger.Message) error {
	l.mutex.RLock()
	defer l.mutex.RUnlock()

	if l.closed {
		return errors.New("client closed")
	}

	if len(bytes.Fields(m.Line)) == 0 {
		level.Debug(l.logger).Log("msg", "ignoring empty line", "line", string(m.Line))
		return nil
	}
	lbs := l.labels.Clone()
	if m.Source != "" {
		lbs["source"] = model.LabelValue(m.Source)
	}
	l.handler.Chan() <- api.Entry{
		Labels: lbs,
		Entry: logproto.Entry{
			Timestamp: m.Timestamp,
			Line:      string(m.Line),
		},
	}
	return nil
}

// Log implements `logger.Logger`
func (l *loki) Name() string {
	return driverName
}

// Log implements `logger.Logger`
func (l *loki) Close() error {
	l.mutex.Lock()
	defer l.mutex.Unlock()
	l.stop()
	l.client.StopNow()
	l.closed = true
	return nil
}
<commit_msg>Removes docker driver empty log line message (#4396)<commit_after>package main

import (
	"bytes"
	"sync"

	"github.com/docker/docker/daemon/logger"
	"github.com/go-kit/kit/log"
	"github.com/pkg/errors"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/common/model"

	"github.com/grafana/loki/clients/pkg/logentry/stages"
	"github.com/grafana/loki/clients/pkg/promtail/api"
	"github.com/grafana/loki/clients/pkg/promtail/client"

	"github.com/grafana/loki/pkg/logproto"
)

var jobName = "docker"

type loki struct {
	client  client.Client
	handler api.EntryHandler
	labels  model.LabelSet
	logger  log.Logger

	closed bool
	mutex  sync.RWMutex

	stop func()
}

// New create a new Loki logger that forward logs to Loki instance
func New(logCtx logger.Info, logger log.Logger) (logger.Logger, error) {
	logger = log.With(logger, "container_id", logCtx.ContainerID)
	cfg, err := parseConfig(logCtx)
	if err != nil {
		return nil, err
	}
	c, err := client.New(prometheus.DefaultRegisterer, cfg.clientConfig, logger)
	if err != nil {
		return nil, err
	}
	var handler api.EntryHandler = c
	var stop = func() {}
	if len(cfg.pipeline.PipelineStages) != 0 {
		pipeline, err := stages.NewPipeline(logger, cfg.pipeline.PipelineStages, &jobName, prometheus.DefaultRegisterer)
		if err != nil {
			return nil, err
		}
		handler = pipeline.Wrap(c)
		stop = handler.Stop
	}
	return &loki{
		client:  c,
		labels:  cfg.labels,
		logger:  logger,
		handler: handler,
		stop:    stop,
	}, nil
}

// Log implements `logger.Logger`
func (l *loki) Log(m *logger.Message) error {
	l.mutex.RLock()
	defer l.mutex.RUnlock()

	if l.closed {
		return errors.New("client closed")
	}

	if len(bytes.Fields(m.Line)) == 0 {
		return nil
	}
	lbs := l.labels.Clone()
	if m.Source != "" {
		lbs["source"] = model.LabelValue(m.Source)
	}
	l.handler.Chan() <- api.Entry{
		Labels: lbs,
		Entry: logproto.Entry{
			Timestamp: m.Timestamp,
			Line:      string(m.Line),
		},
	}
	return nil
}

// Log implements `logger.Logger`
func (l *loki) Name() string {
	return driverName
}

// Log implements `logger.Logger`
func (l *loki) Close() error {
	l.mutex.Lock()
	defer l.mutex.Unlock()
	l.stop()
	l.client.StopNow()
	l.closed = true
	return nil
}
<|endoftext|>


<commit_before>package commands

import (
	"io"
	"io/ioutil"
	"os"
	"text/tabwriter"

	"github.com/golang/protobuf/proto"
	"github.com/spf13/cobra"
	pb "github.com/stevvooe/continuity/proto"
)

var (
	MainCmd = &cobra.Command{
		Use:   "continuity <command>",
		Short: "A transport-agnostic filesytem metadata tool.",
	}

	// usageTemplate is nearly identical to the default template without the
	// automatic addition of flags. Instead, Command.Use is used unmodified.
	usageTemplate = `{{ $cmd := . }}
Usage: {{if .Runnable}}
  {{.UseLine}}{{end}}{{if gt .Aliases 0}}

Aliases:
  {{.NameAndAliases}}
{{end}}{{if .HasExample}}

Examples:
{{ .Example }}{{end}}{{ if .HasNonHelpSubCommands}}

Available Commands: {{range .Commands}}{{if (not .IsHelpCommand)}}
  {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{ if .HasLocalFlags}}

Flags:
{{.LocalFlags.FlagUsages}}{{end}}{{ if .HasInheritedFlags}}

Global Flags:
{{.InheritedFlags.FlagUsages}}{{end}}{{if .HasHelpSubCommands}}

Additional help topics: {{range .Commands}}{{if .IsHelpCommand}}
  {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}}{{end}}{{end}}{{ if .HasSubCommands }}

Use "{{.CommandPath}} [command] --help" for more information about a command.
{{end}}`
)

func init() {
	MainCmd.AddCommand(BuildCmd)
	MainCmd.AddCommand(VerifyCmd)
	MainCmd.AddCommand(ApplyCmd)
	MainCmd.AddCommand(LSCmd)
	MainCmd.AddCommand(StatsCmd)
	MainCmd.AddCommand(DumpCmd)
	MainCmd.AddCommand(MountCmd)
	MainCmd.SetUsageTemplate(usageTemplate)
}

// readManifestFile reads the manifest from the given path. This should
// probably be provided by the continuity library.
func readManifestFile(path string) (*pb.Manifest, error) {
	p, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}

	var bm pb.Manifest

	if err := proto.Unmarshal(p, &bm); err != nil {
		return nil, err
	}

	return &bm, nil
}

// newTabwriter provides a common tabwriter with defaults.
func newTabwriter(w io.Writer) *tabwriter.Writer {
	return tabwriter.NewWriter(os.Stdout, 0, 2, 2, ' ', 0)
}
<commit_msg>main: update cobra template for latest version<commit_after>package commands

import (
	"io"
	"io/ioutil"
	"os"
	"text/tabwriter"

	"github.com/golang/protobuf/proto"
	"github.com/spf13/cobra"
	pb "github.com/stevvooe/continuity/proto"
)

var (
	MainCmd = &cobra.Command{
		Use:   "continuity <command>",
		Short: "A transport-agnostic filesytem metadata tool.",
	}

	// usageTemplate is nearly identical to the default template without the
	// automatic addition of flags. Instead, Command.Use is used unmodified.
	usageTemplate = `Usage:{{if .Runnable}}
  {{.UseLine}}{{end}}{{if .HasSubCommands}}
  {{ .CommandPath}} [command]{{end}}{{if gt .Aliases 0}}

Aliases:
  {{.NameAndAliases}}
{{end}}{{if .HasExample}}

Examples:
{{ .Example }}{{end}}{{ if .HasAvailableSubCommands}}

Available Commands:{{range .Commands}}{{if .IsAvailableCommand}}
  {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{ if .HasLocalFlags}}

Flags:
{{.LocalFlags.FlagUsages | trimRightSpace}}{{end}}{{ if .HasInheritedFlags}}

Global Flags:
{{.InheritedFlags.FlagUsages | trimRightSpace}}{{end}}{{if .HasHelpSubCommands}}

Additional help topics:{{range .Commands}}{{if .IsHelpCommand}}
  {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{ if .HasSubCommands }}

Use "{{.CommandPath}} [command] --help" for more information about a command.{{end}}
`
)

func init() {
	MainCmd.AddCommand(BuildCmd)
	MainCmd.AddCommand(VerifyCmd)
	MainCmd.AddCommand(ApplyCmd)
	MainCmd.AddCommand(LSCmd)
	MainCmd.AddCommand(StatsCmd)
	MainCmd.AddCommand(DumpCmd)
	MainCmd.AddCommand(MountCmd)
	MainCmd.SetUsageTemplate(usageTemplate)
}

// readManifestFile reads the manifest from the given path. This should
// probably be provided by the continuity library.
func readManifestFile(path string) (*pb.Manifest, error) {
	p, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}

	var bm pb.Manifest

	if err := proto.Unmarshal(p, &bm); err != nil {
		return nil, err
	}

	return &bm, nil
}

// newTabwriter provides a common tabwriter with defaults.
func newTabwriter(w io.Writer) *tabwriter.Writer {
	return tabwriter.NewWriter(os.Stdout, 0, 2, 2, ' ', 0)
}
<|endoftext|>


<commit_before>package mux

import (
	"bytes"
	"io"

	"github.com/gorilla/websocket"
	"github.com/yinqiwen/gsnova/common/logger"
)

type WsConn struct {
	*websocket.Conn
	readbuf bytes.Buffer
}

func (ws *WsConn) Write(p []byte) (int, error) {
	c := ws.Conn
	if nil == c {
		return 0, io.EOF
	}
	err := c.WriteMessage(websocket.BinaryMessage, p)
	if nil != err {
		logger.Error("Failed to write websocket binary messgage:%v", err)
		return 0, err
	}
	return len(p), nil
}

func (ws *WsConn) Read(p []byte) (int, error) {
	if nil == ws.Conn {
		return 0, io.EOF
	}
	if ws.readbuf.Len() > 0 {
		return ws.readbuf.Read(p)
	}
	ws.readbuf.Reset()
	c := ws.Conn
	if nil == c {
		return 0, io.EOF
	}
	mt, data, err := c.ReadMessage()
	if err != nil {
		return 0, err
	}
	switch mt {
	case websocket.BinaryMessage:
		ws.readbuf.Write(data)
		return ws.readbuf.Read(p)
	default:
		logger.Error("Invalid websocket message type:%d", mt)
		return 0, io.EOF
	}
}
<commit_msg>reduce once data copy<commit_after>package mux

import (
	"io"

	"github.com/gorilla/websocket"
	"github.com/yinqiwen/gsnova/common/logger"
)

type WsConn struct {
	*websocket.Conn
	msgReader io.Reader
}

func (ws *WsConn) Write(p []byte) (int, error) {
	c := ws.Conn
	if nil == c {
		return 0, io.EOF
	}
	err := c.WriteMessage(websocket.BinaryMessage, p)
	if nil != err {
		logger.Error("Failed to write websocket binary messgage:%v", err)
		return 0, err
	}
	return len(p), nil
}

func (ws *WsConn) readData(p []byte) (int, error) {
	if nil == ws.msgReader {
		return 0, io.EOF
	}
	n, err := ws.msgReader.Read(p)
	if nil != err {
		ws.msgReader = nil
	}
	return n, nil
}

func (ws *WsConn) Read(p []byte) (int, error) {
	if nil != ws.msgReader {
		return ws.readData(p)
	}
	if nil == ws.Conn {
		return 0, io.EOF
	}
	mt, reader, err := ws.Conn.NextReader()
	//mt, data, err := c.ReadMessage()
	if err != nil {
		return 0, err
	}
	switch mt {
	case websocket.BinaryMessage:
		ws.msgReader = reader
		return ws.readData(p)
	default:
		logger.Error("Invalid websocket message type:%d", mt)
		return 0, io.EOF
	}

}
<|endoftext|>


<commit_before>// +build linux

package kcp

import (
	"net"
	"sync/atomic"

	"golang.org/x/net/ipv4"
	"golang.org/x/net/ipv6"
)

func (s *UDPSession) txLoop() {
	addr, _ := net.ResolveUDPAddr("udp", s.conn.LocalAddr().String())
	if addr.IP.To4() != nil {
		s.txLoopIPv4()
	} else {
		s.txLoopIPv6()
	}
}

func (s *UDPSession) txLoopIPv4() {
	conn := ipv4.NewPacketConn(s.conn)
	msgs := make([]ipv4.Message, batchSize)

	for {
		select {
		case txqueue := <-s.chTxQueue:
			if len(txqueue) > 0 {
				nbytes := 0

				for k := range txqueue {
					idx := k % batchSize
					msgs[idx].Addr = s.remote
					msgs[idx].Buffers = [][]byte{txqueue[k]}
					nbytes += len(txqueue[k])

					if (k+1)%batchSize == 0 {
						if _, err := conn.WriteBatch(msgs, 0); err != nil {
							s.notifyWriteError(err)
						}
					}
				}

				if remain := len(txqueue) % batchSize; remain > 0 {
					if _, err := conn.WriteBatch(msgs[:remain], 0); err != nil {
						s.notifyWriteError(err)
					}
				}

				for k := range txqueue {
					xmitBuf.Put(txqueue[k])
				}

				atomic.AddUint64(&DefaultSnmp.OutPkts, uint64(len(txqueue)))
				atomic.AddUint64(&DefaultSnmp.OutBytes, uint64(nbytes))
			}
		case <-s.die:
			return
		}
	}
}

func (s *UDPSession) txLoopIPv6() {
	conn := ipv6.NewPacketConn(s.conn)
	msgs := make([]ipv6.Message, batchSize)

	for {
		select {
		case txqueue := <-s.chTxQueue:
			if len(txqueue) > 0 {
				nbytes := 0

				for k := range txqueue {
					idx := k % batchSize
					msgs[idx].Addr = s.remote
					msgs[idx].Buffers = [][]byte{txqueue[k]}
					nbytes += len(txqueue[k])

					if (k+1)%batchSize == 0 {
						if _, err := conn.WriteBatch(msgs, 0); err != nil {
							s.notifyWriteError(err)
						}
					}
				}

				if remain := len(txqueue) % batchSize; remain > 0 {
					if _, err := conn.WriteBatch(msgs[:remain], 0); err != nil {
						s.notifyWriteError(err)
					}
				}

				for k := range txqueue {
					xmitBuf.Put(txqueue[k])
				}

				atomic.AddUint64(&DefaultSnmp.OutPkts, uint64(len(txqueue)))
				atomic.AddUint64(&DefaultSnmp.OutBytes, uint64(nbytes))
			}
		case <-s.die:
			return
		}
	}
}
<commit_msg>loop on sendmmsg<commit_after>// +build linux

package kcp

import (
	"net"
	"sync/atomic"

	"golang.org/x/net/ipv4"
	"golang.org/x/net/ipv6"
)

func (s *UDPSession) txLoop() {
	addr, _ := net.ResolveUDPAddr("udp", s.conn.LocalAddr().String())
	if addr.IP.To4() != nil {
		s.txLoopIPv4()
	} else {
		s.txLoopIPv6()
	}
}

func (s *UDPSession) txLoopIPv4() {
	conn := ipv4.NewPacketConn(s.conn)
	msgs := make([]ipv4.Message, batchSize)

	for {
		select {
		case txqueue := <-s.chTxQueue:
			if len(txqueue) > 0 {
				nbytes := 0

				for k := range txqueue {
					idx := k % batchSize
					msgs[idx].Addr = s.remote
					msgs[idx].Buffers = [][]byte{txqueue[k]}
					nbytes += len(txqueue[k])

					if (k+1)%batchSize == 0 {
						vec := msgs
						for len(vec) > 0 {
							if n, err := conn.WriteBatch(vec, 0); err == nil {
								vec = vec[n:]
							} else {
								s.notifyWriteError(err)
								break
							}
						}
					}
				}

				if remain := len(txqueue) % batchSize; remain > 0 {
					vec := msgs[:remain]
					for len(vec) > 0 {
						if n, err := conn.WriteBatch(vec, 0); err == nil {
							vec = vec[n:]
						} else {
							s.notifyWriteError(err)
							break
						}
					}
				}

				for k := range txqueue {
					xmitBuf.Put(txqueue[k])
				}

				atomic.AddUint64(&DefaultSnmp.OutPkts, uint64(len(txqueue)))
				atomic.AddUint64(&DefaultSnmp.OutBytes, uint64(nbytes))
			}
		case <-s.die:
			return
		}
	}
}

func (s *UDPSession) txLoopIPv6() {
	conn := ipv6.NewPacketConn(s.conn)
	msgs := make([]ipv6.Message, batchSize)

	for {
		select {
		case txqueue := <-s.chTxQueue:
			if len(txqueue) > 0 {
				nbytes := 0

				for k := range txqueue {
					idx := k % batchSize
					msgs[idx].Addr = s.remote
					msgs[idx].Buffers = [][]byte{txqueue[k]}
					nbytes += len(txqueue[k])

					if (k+1)%batchSize == 0 {
						vec := msgs
						for len(vec) > 0 {
							if n, err := conn.WriteBatch(vec, 0); err == nil {
								vec = vec[n:]
							} else {
								s.notifyWriteError(err)
								break
							}
						}
					}
				}

				if remain := len(txqueue) % batchSize; remain > 0 {
					vec := msgs[:remain]
					for len(vec) > 0 {
						if n, err := conn.WriteBatch(vec, 0); err == nil {
							vec = vec[n:]
						} else {
							s.notifyWriteError(err)
							break
						}
					}
				}

				for k := range txqueue {
					xmitBuf.Put(txqueue[k])
				}

				atomic.AddUint64(&DefaultSnmp.OutPkts, uint64(len(txqueue)))
				atomic.AddUint64(&DefaultSnmp.OutBytes, uint64(nbytes))
			}

		case <-s.die:
			return
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"

	"log"
	"time"

	"bytes"
	"strings"

	"io/ioutil"
	"os"

	"github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
	"github.com/coreos/etcd/client"
)

var (
	endpoint   = flag.String("etcd", "http://127.0.0.1:2379", "Specifies the etcd endpoint")
	outputDir  = flag.String("outputDir", "/run/conf", "Specifies the output dir")
	etcdPrefix = flag.String("etcdPrefix", "", "Specifies the etcd prefix")
)

func getKeyName(node *client.Node) string {
	keySplitted := strings.Split(node.Key, "/")
	return keySplitted[len(keySplitted)-1]
}

func getConfigFile(configDirectory *client.Node) []byte {
	var buffer bytes.Buffer

	for _, key := range configDirectory.Nodes {
		envVariable := getKeyName(key)

		buffer.WriteString(envVariable + "=" + key.Value + "\n")
	}
	return buffer.Bytes()
}

func traverseConfigDirectory(configDirectories *client.Node) {
	for _, configNode := range configDirectories.Nodes {
		configFileName := (*outputDir) + "/" + getKeyName(configNode)
		ioutil.WriteFile(configFileName, getConfigFile(configNode), 0644)

		log.Print("Generated config file " + configFileName)
	}
}

func generateConfig(c client.Client) {
	kapi := client.NewKeysAPI(c)

	for {
		resp, err := kapi.Get(context.Background(), *etcdPrefix, &client.GetOptions{Recursive: true})
		panicOnError(err)
		traverseConfigDirectory(resp.Node)

		watcher := kapi.Watcher(*etcdPrefix, &client.WatcherOptions{Recursive: true, AfterIndex: resp.Index})
		ctx := context.Background()

		resp, err = watcher.Next(ctx)

		panicOnError(err)
	}
}

func dirExists(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return true, err
}

func panicOnError(err error) {
	if err != nil {
		log.Fatal(err)
		panic(err)
	}
}

func main() {
	flag.Parse()

	exists, err := dirExists(*outputDir)
	panicOnError(err)

	if !exists {
		err := os.Mkdir(*outputDir, 0755)
		panicOnError(err)
	}

	cfg := client.Config{
		Endpoints: []string{*endpoint},
		Transport: client.DefaultTransport,
		// set timeout per request to fail fast when the target endpoint is unavailable
		HeaderTimeoutPerRequest: time.Second,
	}

	c, err := client.New(cfg)
	panicOnError(err)

	generateConfig(c)
}
<commit_msg>Add default option for `-etcdPrefix`<commit_after>package main

import (
	"flag"

	"log"
	"time"

	"bytes"
	"strings"

	"io/ioutil"
	"os"

	"github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
	"github.com/coreos/etcd/client"
)

var (
	endpoint   = flag.String("etcd", "http://127.0.0.1:2379", "Specifies the etcd endpoint")
	outputDir  = flag.String("outputDir", "/run/conf", "Specifies the output dir")
	etcdPrefix = flag.String("etcdPrefix", "/conf", "Specifies the etcd prefix")
)

func getKeyName(node *client.Node) string {
	keySplitted := strings.Split(node.Key, "/")
	return keySplitted[len(keySplitted)-1]
}

func getConfigFile(configDirectory *client.Node) []byte {
	var buffer bytes.Buffer

	for _, key := range configDirectory.Nodes {
		envVariable := getKeyName(key)

		buffer.WriteString(envVariable + "=" + key.Value + "\n")
	}
	return buffer.Bytes()
}

func traverseConfigDirectory(configDirectories *client.Node) {
	for _, configNode := range configDirectories.Nodes {
		configFileName := (*outputDir) + "/" + getKeyName(configNode)
		ioutil.WriteFile(configFileName, getConfigFile(configNode), 0644)

		log.Print("Generated config file " + configFileName)
	}
}

func generateConfig(c client.Client) {
	kapi := client.NewKeysAPI(c)

	for {
		resp, err := kapi.Get(context.Background(), *etcdPrefix, &client.GetOptions{Recursive: true})
		panicOnError(err)
		traverseConfigDirectory(resp.Node)

		watcher := kapi.Watcher(*etcdPrefix, &client.WatcherOptions{Recursive: true, AfterIndex: resp.Index})
		ctx := context.Background()

		resp, err = watcher.Next(ctx)

		panicOnError(err)
	}
}

func dirExists(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return true, err
}

func panicOnError(err error) {
	if err != nil {
		log.Fatal(err)
		panic(err)
	}
}

func main() {
	flag.Parse()

	exists, err := dirExists(*outputDir)
	panicOnError(err)

	if !exists {
		err := os.Mkdir(*outputDir, 0755)
		panicOnError(err)
	}

	cfg := client.Config{
		Endpoints: []string{*endpoint},
		Transport: client.DefaultTransport,
		// set timeout per request to fail fast when the target endpoint is unavailable
		HeaderTimeoutPerRequest: time.Second,
	}

	c, err := client.New(cfg)
	panicOnError(err)

	generateConfig(c)
}
<|endoftext|>


<commit_before>package ui

// This should (a) evolve to handle arbitrary groups, and (b) move into skypies/util

import(
	"net/http"
	"strings"

	"golang.org/x/net/context"

	"github.com/skypies/complaints/config"
)

var(
	adminEmails = map[string]int{}
)

func init() {
	//AdminEmails := map[string]int{}
	for _,e := range strings.Split(config.Get("users.admin"), ",") {
		adminEmails[strings.ToLower(e)] = 1
	}
}

// I'm not sure this should ever be needed outside of this file, but hey.
func IsAdmin(email string) bool {
	_,exists := adminEmails[strings.ToLower(email)]
	return exists
}

// Convenience combos
func WithCtxAdmin(ch contextHandler) baseHandler {
	return WithCtx(WithSession(WithAdmin(ch), authFallback))
}
func WithCtxTlsAdmin(ch contextHandler) baseHandler {
	return WithCtx(WithTLS(WithSession(WithAdmin(ch), authFallback)))
}

func HasAdmin(bh baseHandler) baseHandler {
	return WithCtx(WithSession(WithAdmin(WithoutCtx(bh)), authFallback))
}

// WithAdmin validates that the request has admin privileges, and runs the handler (or returns 401).
// Privileges are either that the user is logged in, and is an admin; or that the request came from
// an appengine cron job.
// https://cloud.google.com/appengine/docs/flexible/nodejs/scheduling-jobs-with-cron-yaml#validating_cron_requests
func WithAdmin(ch contextHandler) contextHandler {
	return func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
		cron := r.Header.Get("x-appengine-cron")
		sesh,hadSesh := GetUserSession(ctx)

		if cron == "" && (!hadSesh || !IsAdmin(sesh.Email)) {
			http.Error(w, "This URL requires admin access", http.StatusUnauthorized)
			return
		}

		// We have admin rights - run the handler
		ch(ctx,w,r)
	}
}

func authFallback(ctx context.Context, w http.ResponseWriter, r *http.Request) {
	http.Error(w, "This URL requires you to be logged in", http.StatusUnauthorized)
}
<commit_msg>Fix bug that broke the cron-access mode. Refactored to make logic simpler.<commit_after>package ui

// This should (a) evolve to handle arbitrary groups, and (b) move into skypies/util

import(
	"net/http"
	"strings"

	"golang.org/x/net/context"

	"github.com/skypies/complaints/config"
)

var(
	adminEmails = map[string]int{}
)

func init() {
	//AdminEmails := map[string]int{}
	for _,e := range strings.Split(config.Get("users.admin"), ",") {
		adminEmails[strings.ToLower(e)] = 1
	}
}

// I'm not sure this should ever be needed outside of this file, but hey.
func IsAdmin(email string) bool {
	_,exists := adminEmails[strings.ToLower(email)]
	return exists
}

// Convenience combos.
// In all of them, use the main handler as the authFallback too, because we want to run it whether we find a
// usersession or not.
func WithCtxAdmin(ch contextHandler) baseHandler {
	return WithCtx(WithSession(WithAdmin(ch), WithAdmin(ch)))
}
func WithCtxTlsAdmin(ch contextHandler) baseHandler {
	return WithCtx(WithTLS(WithSession(WithAdmin(ch), WithAdmin(ch))))
}
func HasAdmin(bh baseHandler) baseHandler {
	handler := WithAdmin(WithoutCtx(bh))
	return WithCtx(WithSession(handler, handler))
}

// WithAdmin validates that the request has admin privileges, and runs the handler (or returns 401).
// Privileges are either that the user is logged in, and is an admin; or that the request came from
// an appengine cron job.
// https://cloud.google.com/appengine/docs/flexible/nodejs/scheduling-jobs-with-cron-yaml#validating_cron_requests
func WithAdmin(ch contextHandler) contextHandler {
	return func(ctx context.Context, w http.ResponseWriter, r *http.Request) {
		cron := r.Header.Get("x-appengine-cron")
		sesh,hadSesh := GetUserSession(ctx)
		//log.Printf("WithAdmin: hadSesh=%v, sesh=%#v, cron=%q", hadSesh, sesh, cron)

		haveAdmin := false
		if cron != "" {
			haveAdmin = true
		} else if hadSesh && IsAdmin(sesh.Email) {
			haveAdmin = true
		}

		if !haveAdmin {
			errstr := "This URL requires you to be logged in"
			if hadSesh {
				errstr = "This URL requires admin access"
			}
			http.Error(w, errstr, http.StatusUnauthorized)
			return
		}

		// We have admin rights - run the handler
		ch(ctx,w,r)
	}
}
<|endoftext|>


<commit_before>// Copyright 2017 Frédéric Guillot. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.

package ui // import "miniflux.app/ui"

import (
	"encoding/base64"
	"errors"
	"net/http"
	"time"

	"miniflux.app/config"
	"miniflux.app/crypto"
	"miniflux.app/http/client"
	"miniflux.app/http/request"
	"miniflux.app/http/response"
	"miniflux.app/http/response/html"
)

func (h *handler) imageProxy(w http.ResponseWriter, r *http.Request) {
	// If we receive a "If-None-Match" header, we assume the image is already stored in browser cache.
	if r.Header.Get("If-None-Match") != "" {
		w.WriteHeader(http.StatusNotModified)
		return
	}

	encodedURL := request.RouteStringParam(r, "encodedURL")
	if encodedURL == "" {
		html.BadRequest(w, r, errors.New("No URL provided"))
		return
	}

	decodedURL, err := base64.URLEncoding.DecodeString(encodedURL)
	if err != nil {
		html.BadRequest(w, r, errors.New("Unable to decode this URL"))
		return
	}

	req, err := http.NewRequest("GET", string(decodedURL), nil)
	if err != nil {
		html.ServerError(w, r, err)
		return
	}
	req.Header.Add("User-Agent", client.DefaultUserAgent)
	req.Header.Add("Connection", "close")

	clt := &http.Client{
		Timeout: time.Duration(config.Opts.HTTPClientTimeout()) * time.Second,
	}

	resp, err := clt.Do(req)
	if err != nil {
		html.ServerError(w, r, err)
		return
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		html.NotFound(w, r)
		return
	}

	etag := crypto.HashFromBytes(decodedURL)

	response.New(w, r).WithCaching(etag, 72*time.Hour, func(b *response.Builder) {
		b.WithHeader("Content-Type", resp.Header.Get("Content-Type"))
		b.WithBody(resp.Body)
		b.WithoutCompression()
		b.Write()
	})
}
<commit_msg>Log image proxy URL in debug mode<commit_after>// Copyright 2017 Frédéric Guillot. All rights reserved.
// Use of this source code is governed by the Apache 2.0
// license that can be found in the LICENSE file.

package ui // import "miniflux.app/ui"

import (
	"encoding/base64"
	"errors"
	"net/http"
	"time"

	"miniflux.app/config"
	"miniflux.app/crypto"
	"miniflux.app/http/client"
	"miniflux.app/http/request"
	"miniflux.app/http/response"
	"miniflux.app/http/response/html"
	"miniflux.app/logger"
)

func (h *handler) imageProxy(w http.ResponseWriter, r *http.Request) {
	// If we receive a "If-None-Match" header, we assume the image is already stored in browser cache.
	if r.Header.Get("If-None-Match") != "" {
		w.WriteHeader(http.StatusNotModified)
		return
	}

	encodedURL := request.RouteStringParam(r, "encodedURL")
	if encodedURL == "" {
		html.BadRequest(w, r, errors.New("No URL provided"))
		return
	}

	decodedURL, err := base64.URLEncoding.DecodeString(encodedURL)
	if err != nil {
		html.BadRequest(w, r, errors.New("Unable to decode this URL"))
		return
	}

	imageURL := string(decodedURL)
	logger.Debug(`[Proxy] Fetching %q`, imageURL)

	req, err := http.NewRequest("GET", imageURL, nil)
	if err != nil {
		html.ServerError(w, r, err)
		return
	}
	req.Header.Add("User-Agent", client.DefaultUserAgent)
	req.Header.Add("Connection", "close")

	clt := &http.Client{
		Timeout: time.Duration(config.Opts.HTTPClientTimeout()) * time.Second,
	}

	resp, err := clt.Do(req)
	if err != nil {
		html.ServerError(w, r, err)
		return
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		html.NotFound(w, r)
		return
	}

	etag := crypto.HashFromBytes(decodedURL)

	response.New(w, r).WithCaching(etag, 72*time.Hour, func(b *response.Builder) {
		b.WithHeader("Content-Type", resp.Header.Get("Content-Type"))
		b.WithBody(resp.Body)
		b.WithoutCompression()
		b.Write()
	})
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package node

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"

	jose "github.com/square/go-jose"
	kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
)

func RetrieveTrustedClusterInfo(s *kubeadmapi.NodeConfiguration) (*kubeadmapi.ClusterInfo, error) {
	host, port := s.MasterAddresses[0], 9898
	requestURL := fmt.Sprintf("http://%s:%d/cluster-info/v1/?token-id=%s", host, port, s.Secrets.TokenID)
	req, err := http.NewRequest("GET", requestURL, nil)
	if err != nil {
		return nil, fmt.Errorf("<node/discovery> failed to consturct an HTTP request [%v]", err)
	}

	fmt.Printf("<node/discovery> created cluster info discovery client, requesting info from %q\n", requestURL)

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, fmt.Errorf("<node/discovery> failed to request cluster info [%v]", err)
	}
	buf := new(bytes.Buffer)
	io.Copy(buf, res.Body)
	res.Body.Close()

	object, err := jose.ParseSigned(buf.String())
	if err != nil {
		return nil, fmt.Errorf("<node/discovery> failed to parse response as JWS object [%v]", err)
	}

	fmt.Println("<node/discovery> cluster info object received, verifying signature using given token")

	output, err := object.Verify(s.Secrets.Token)
	if err != nil {
		return nil, fmt.Errorf("<node/discovery> failed to verify JWS signature of received cluster info object [%v]", err)
	}

	clusterInfo := kubeadmapi.ClusterInfo{}

	if err := json.Unmarshal(output, &clusterInfo); err != nil {
		return nil, fmt.Errorf("<node/discovery> failed to decode received cluster info object [%v]", err)
	}

	if len(clusterInfo.CertificateAuthorities) == 0 || len(clusterInfo.Endpoints) == 0 {
		return nil, fmt.Errorf("<node/discovery> cluster info object is invalid - no endpoint(s) and/or root CA certificate(s) found")
	}

	// TODO(phase1+) print summary info about the CA certificate, along with the the checksum signature
	// we also need an ability for the user to configure the client to validate recieved CA cert agains a checksum
	fmt.Printf("<node/discovery> cluster info signature and contents are valid, will use API endpoints %v\n", clusterInfo.Endpoints)
	return &clusterInfo, nil
}
<commit_msg>Fix simple typos.<commit_after>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package node

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"

	jose "github.com/square/go-jose"
	kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
)

func RetrieveTrustedClusterInfo(s *kubeadmapi.NodeConfiguration) (*kubeadmapi.ClusterInfo, error) {
	host, port := s.MasterAddresses[0], 9898
	requestURL := fmt.Sprintf("http://%s:%d/cluster-info/v1/?token-id=%s", host, port, s.Secrets.TokenID)
	req, err := http.NewRequest("GET", requestURL, nil)
	if err != nil {
		return nil, fmt.Errorf("<node/discovery> failed to consturct an HTTP request [%v]", err)
	}

	fmt.Printf("<node/discovery> created cluster info discovery client, requesting info from %q\n", requestURL)

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, fmt.Errorf("<node/discovery> failed to request cluster info [%v]", err)
	}
	buf := new(bytes.Buffer)
	io.Copy(buf, res.Body)
	res.Body.Close()

	object, err := jose.ParseSigned(buf.String())
	if err != nil {
		return nil, fmt.Errorf("<node/discovery> failed to parse response as JWS object [%v]", err)
	}

	fmt.Println("<node/discovery> cluster info object received, verifying signature using given token")

	output, err := object.Verify(s.Secrets.Token)
	if err != nil {
		return nil, fmt.Errorf("<node/discovery> failed to verify JWS signature of received cluster info object [%v]", err)
	}

	clusterInfo := kubeadmapi.ClusterInfo{}

	if err := json.Unmarshal(output, &clusterInfo); err != nil {
		return nil, fmt.Errorf("<node/discovery> failed to decode received cluster info object [%v]", err)
	}

	if len(clusterInfo.CertificateAuthorities) == 0 || len(clusterInfo.Endpoints) == 0 {
		return nil, fmt.Errorf("<node/discovery> cluster info object is invalid - no endpoint(s) and/or root CA certificate(s) found")
	}

	// TODO(phase1+) print summary info about the CA certificate, along with the the checksum signature
	// we also need an ability for the user to configure the client to validate received CA cert against a checksum
	fmt.Printf("<node/discovery> cluster info signature and contents are valid, will use API endpoints %v\n", clusterInfo.Endpoints)
	return &clusterInfo, nil
}
<|endoftext|>


<commit_before>// +build linux

/*
© Copyright IBM Corporation 2017, 2018

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 main

import (
	"fmt"

	"golang.org/x/sys/unix"
)

// fsTypes contains file system identifier codes.
// This code will not compile on some operating systems - Linux only.
var fsTypes = map[int64]string{
	0x61756673: "aufs",
	0xef53:     "ext",
	0x6969:     "nfs",
	0x65735546: "fuse",
	0x9123683e: "btrfs",
	0x01021994: "tmpfs",
	0x794c7630: "overlayfs",
	0x58465342: "xfs",
}

func checkFS(path string) error {
	statfs := &unix.Statfs_t{}
	err := unix.Statfs(path, statfs)
	if err != nil {
		log.Println(err)
		return nil
	}
	// Use a type conversion to make type an int64.  On s390x it's a uint32.
	t, ok := fsTypes[int64(statfs.Type)]
	if !ok {
		log.Printf("WARNING: detected %v has unknown filesystem type %x", path, statfs.Type)
		return nil
	}
	switch t {
	case "aufs", "overlayfs", "tmpfs":
		return fmt.Errorf("%v uses unsupported filesystem type: %v", path, t)
	default:
		log.Printf("Detected %v has filesystem type '%v'", path, t)
		return nil
	}
}
<commit_msg>Extend fs type list for linux<commit_after>// +build linux

/*
© Copyright IBM Corporation 2017, 2018

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 main

import (
	"fmt"

	"golang.org/x/sys/unix"
)

// fsTypes contains file system identifier codes.
// This code will not compile on some operating systems - Linux only.
var fsTypes = map[int64]string{
	0x61756673: "aufs",
	0xef53:     "ext",
	0x6969:     "nfs",
	0x65735546: "fuse",
	0x9123683e: "btrfs",
	0x01021994: "tmpfs",
	0x794c7630: "overlayfs",
	0x58465342: "xfs",
	// less popular codes
	0xadf5:     "adfs",
	0xadff:     "affs",
	0x5346414F: "afs",
	0x0187:     "autofs",
	0x73757245: "coda",
	0x28cd3d45: "cramfs",
	0x453dcd28: "cramfs",
	0x64626720: "debugfs",
	0x73636673: "securityfs",
	0xf97cff8c: "selinux",
	0x43415d53: "smack",
	0x858458f6: "ramfs",
	0x958458f6: "hugetlbfs",
	0x73717368: "squashfs",
	0xf15f:     "ecryptfs",
	0x414A53:   "efs",
	0xabba1974: "xenfs",
	0x3434:     "nilfs",
	0xF2F52010: "f2fs",
	0xf995e849: "hpfs",
	0x9660:     "isofs",
	0x72b6:     "jffs2",
	0x6165676C: "pstorefs",
	0xde5e81e4: "efivarfs",
	0x00c0ffee: "hostfs",
	0x137F:     "minix_14",  // minix v1 fs, 14 char names
	0x138F:     "minix_30",  // minix v1 fs, 30 char names
	0x2468:     "minix2_14", // minix v2 fs, 14 char names
	0x2478:     "minix2_30", // minix v2 fs, 30 char names
	0x4d5a:     "minix3_60", // minix v3 fs, 60 char names
	0x4d44:     "msdos",
	0x564c:     "ncp",
	0x7461636f: "ocfs2",
	0x9fa1:     "openprom",
	0x002f:     "qnx4",
	0x68191122: "qnx6",
	0x6B414653: "afs_fs",
	0x52654973: "reiserfs",
	0x517B:     "smb",
	0x27e0eb:   "cgroup",
	0x63677270: "cgroup2",
	0x7655821:  "rdtgroup",
	0x57AC6E9D: "stack_end",
	0x74726163: "tracefs",
	0x01021997: "v9fs",
	0x62646576: "bdevfs",
	0x64646178: "daxfs",
	0x42494e4d: "binfmtfs",
	0x1cd1:     "devpts",
	0xBAD1DEA:  "futexfs",
	0x50495045: "pipefs",
	0x9fa0:     "proc",
	0x534F434B: "sockfs",
	0x62656572: "sysfs",
	0x9fa2:     "usbdevice",
	0x11307854: "mtd_inode",
	0x09041934: "anon_inode",
	0x73727279: "btrfs",
	0x6e736673: "nsfs",
	0xcafe4a11: "bpf",
	0x5a3c69f0: "aafs",
	0x15013346: "udf",
	0x13661366: "balloon_kvm",
	0x58295829: "zsmalloc",
}

func checkFS(path string) error {
	statfs := &unix.Statfs_t{}
	err := unix.Statfs(path, statfs)
	if err != nil {
		log.Println(err)
		return nil
	}
	// Use a type conversion to make type an int64.  On s390x it's a uint32.
	t, ok := fsTypes[int64(statfs.Type)]
	if !ok {
		log.Printf("WARNING: detected %v has unknown filesystem type %x", path, statfs.Type)
		return nil
	}
	switch t {
	case "aufs", "overlayfs", "tmpfs":
		return fmt.Errorf("%v uses unsupported filesystem type: %v", path, t)
	default:
		log.Printf("Detected %v has filesystem type '%v'", path, t)
		return nil
	}
}
<|endoftext|>


<commit_before>// based on encoding/csv
package ltsv

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"unicode"
	"strings"
)

// A Reader reads records from a LTSV-encoded file.
//
// As returned by NewReader, a Reader expects input LTSV-encoded file.
// The exported fields can be changed to customize the details before the
// first call to Read or ReadAll.
//
// Delimiter is the field delimiter.  It defaults to '\t'.
//
// Comment, if not 0, is the comment character. Lines beginning with the
// Comment character are ignored.
type Reader struct {
	Delimiter rune // Field delimiter (set to '\t' by NewReader)
	Comment   rune // Comment character for start of line
	line      int
	r         *bufio.Reader
	label     bytes.Buffer
	field     bytes.Buffer
}

// NewReader returns a new Reader that reads from r.
func NewReader(r io.Reader) *Reader {
	return &Reader{
		Delimiter: '\t',
		r:         bufio.NewReader(r),
	}
}

// Read reads one record from r.  The record is a slice of strings with each
// string representing one field.
func (r *Reader) Read() (map[string]string, error) {
	r.line++
	record := make(map[string]string)
	for {
		// If we are support comments and it is the comment character
		// then skip to the end of line.
		r1, err := r.readRune()
		if r.Comment != 0 && r1 == r.Comment {
			for {
				r1, err := r.readRune()
				if err != nil {
					return nil, err
				} else if r1 == '\n' {
					break
				}
			}
			continue
		}
		r.r.UnreadRune()

		label, err := r.parseLabel()
		if err != nil {
			return nil, err
		}
		if label == "" {
			continue // skip empty label
		}

		field, end, err := r.parseField()
		if err != nil {
			return nil, err
		}

		record[label] = field
		if end {
			return record, nil
		}
	}
	panic("unreachable")
}

func (r *Reader) parseLabel() (string, error) {
	r.label.Reset()
	for {
		r1, err := r.readRune()
		if err != nil {
			return "", err
		} else if r1 == ':' {
			return strings.TrimSpace(r.label.String()), nil
		} else if r1 == '\t' || r1 == '\n' {
			return "", nil // no label
		} else if unicode.IsControl(r1) || !unicode.IsPrint(r1) {
			return "", errors.New(fmt.Sprintf("line %d: invalid rune at label", r.line))
		}
		r.label.WriteRune(r1)
	}
	panic("unreachable")
}

func (r *Reader) parseField() (string, bool, error) {
	r.field.Reset()
	for {
		r1, err := r.readRune()
		if err != nil {
			if err == io.EOF {
				return r.field.String(), true, nil
			}
			return "", false, err
		} else if r1 == '\t' {
			return r.field.String(), false, nil
		} else if r1 == '\n' {
			return r.field.String(), true, nil
		}
		r.field.WriteRune(r1)
	}
	panic("unreachable")
}

func (r *Reader) readRune() (rune, error) {
	r1, _, err := r.r.ReadRune()
	if r1 == '\r' {
		r1, _, err = r.r.ReadRune()
		if err == nil {
			if r1 != '\n' {
				r.r.UnreadRune()
				r1 = '\r'
			}
		}
	}
	return r1, err
}

func (r *Reader) ReadAll() ([]map[string]string, error) {
	records := make([]map[string]string, 0)
	for {
		record, err := r.Read()
		if err == io.EOF {
			return records, nil
		}
		if err != nil {
			return nil, err
		}
		records = append(records, record)
	}
	panic("unreachable")
}
<commit_msg>go fmt<commit_after>// based on encoding/csv
package ltsv

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"strings"
	"unicode"
)

// A Reader reads records from a LTSV-encoded file.
//
// As returned by NewReader, a Reader expects input LTSV-encoded file.
// The exported fields can be changed to customize the details before the
// first call to Read or ReadAll.
//
// Delimiter is the field delimiter.  It defaults to '\t'.
//
// Comment, if not 0, is the comment character. Lines beginning with the
// Comment character are ignored.
type Reader struct {
	Delimiter rune // Field delimiter (set to '\t' by NewReader)
	Comment   rune // Comment character for start of line
	line      int
	r         *bufio.Reader
	label     bytes.Buffer
	field     bytes.Buffer
}

// NewReader returns a new Reader that reads from r.
func NewReader(r io.Reader) *Reader {
	return &Reader{
		Delimiter: '\t',
		r:         bufio.NewReader(r),
	}
}

// Read reads one record from r.  The record is a slice of strings with each
// string representing one field.
func (r *Reader) Read() (map[string]string, error) {
	r.line++
	record := make(map[string]string)
	for {
		// If we are support comments and it is the comment character
		// then skip to the end of line.
		r1, err := r.readRune()
		if r.Comment != 0 && r1 == r.Comment {
			for {
				r1, err := r.readRune()
				if err != nil {
					return nil, err
				} else if r1 == '\n' {
					break
				}
			}
			continue
		}
		r.r.UnreadRune()

		label, err := r.parseLabel()
		if err != nil {
			return nil, err
		}
		if label == "" {
			continue // skip empty label
		}

		field, end, err := r.parseField()
		if err != nil {
			return nil, err
		}

		record[label] = field
		if end {
			return record, nil
		}
	}
	panic("unreachable")
}

func (r *Reader) parseLabel() (string, error) {
	r.label.Reset()
	for {
		r1, err := r.readRune()
		if err != nil {
			return "", err
		} else if r1 == ':' {
			return strings.TrimSpace(r.label.String()), nil
		} else if r1 == '\t' || r1 == '\n' {
			return "", nil // no label
		} else if unicode.IsControl(r1) || !unicode.IsPrint(r1) {
			return "", errors.New(fmt.Sprintf("line %d: invalid rune at label", r.line))
		}
		r.label.WriteRune(r1)
	}
	panic("unreachable")
}

func (r *Reader) parseField() (string, bool, error) {
	r.field.Reset()
	for {
		r1, err := r.readRune()
		if err != nil {
			if err == io.EOF {
				return r.field.String(), true, nil
			}
			return "", false, err
		} else if r1 == '\t' {
			return r.field.String(), false, nil
		} else if r1 == '\n' {
			return r.field.String(), true, nil
		}
		r.field.WriteRune(r1)
	}
	panic("unreachable")
}

func (r *Reader) readRune() (rune, error) {
	r1, _, err := r.r.ReadRune()
	if r1 == '\r' {
		r1, _, err = r.r.ReadRune()
		if err == nil {
			if r1 != '\n' {
				r.r.UnreadRune()
				r1 = '\r'
			}
		}
	}
	return r1, err
}

func (r *Reader) ReadAll() ([]map[string]string, error) {
	records := make([]map[string]string, 0)
	for {
		record, err := r.Read()
		if err == io.EOF {
			return records, nil
		}
		if err != nil {
			return nil, err
		}
		records = append(records, record)
	}
	panic("unreachable")
}
<|endoftext|>


<commit_before>package middleware

import (
	"net/http"
)

func copyHeaders(src, dest http.Header) {
	for k, v := range src {
		dest[k] = v
	}
}

// ErrorHandler lets you call an alternate http handler upon a certain response code.
// Note it will assume a 200 if the wrapped handler does not write anything
type ErrorHandler struct {
	Code    int
	Handler http.Handler
}

// Wrap implements Middleware
func (e ErrorHandler) Wrap(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		i := newErrorInterceptor(w, e.Code)
		next.ServeHTTP(i, r)
		if !i.gotCode {
			i.WriteHeader(http.StatusOK)
		}
		if i.intercepted {
			e.Handler.ServeHTTP(w, r)
		}
	})
}

// errorInterceptor wraps an underlying ResponseWriter and buffers all header changes, until it knows the return code.
// It then passes everything through, unless the code matches the target code, in which case it will discard everything.
type errorInterceptor struct {
	originalWriter http.ResponseWriter
	targetCode     int
	headers        http.Header
	gotCode        bool
	intercepted    bool
}

func newErrorInterceptor(w http.ResponseWriter, code int) *errorInterceptor {
	i := errorInterceptor{originalWriter: w, targetCode: code}
	i.headers = make(http.Header)
	copyHeaders(w.Header(), i.headers)
	return &i
}

// Header implements http.ResponseWriter
func (i errorInterceptor) Header() http.Header {
	return i.headers
}

// WriteHeader implements http.ResponseWriter
func (i errorInterceptor) WriteHeader(code int) {
	if i.gotCode {
		panic("errorInterceptor.WriteHeader() called twice")
	}

	i.gotCode = true
	if code == i.targetCode {
		i.intercepted = true
	} else {
		copyHeaders(i.headers, i.originalWriter.Header())
		i.originalWriter.WriteHeader(code)
	}
}

// Write implements http.ResponseWriter
func (i errorInterceptor) Write(data []byte) (int, error) {
	if !i.gotCode {
		i.WriteHeader(http.StatusOK)
	}
	if !i.intercepted {
		return i.originalWriter.Write(data)
	}
	return len(data), nil
}
<commit_msg>middleware/errorhandler: Fix a bug which meant it never works<commit_after>package middleware

import (
	"net/http"
)

func copyHeaders(src, dest http.Header) {
	for k, v := range src {
		dest[k] = v
	}
}

// ErrorHandler lets you call an alternate http handler upon a certain response code.
// Note it will assume a 200 if the wrapped handler does not write anything
type ErrorHandler struct {
	Code    int
	Handler http.Handler
}

// Wrap implements Middleware
func (e ErrorHandler) Wrap(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		i := newErrorInterceptor(w, e.Code)
		next.ServeHTTP(i, r)
		if !i.gotCode {
			i.WriteHeader(http.StatusOK)
		}
		if i.intercepted {
			e.Handler.ServeHTTP(w, r)
		}
	})
}

// errorInterceptor wraps an underlying ResponseWriter and buffers all header changes, until it knows the return code.
// It then passes everything through, unless the code matches the target code, in which case it will discard everything.
type errorInterceptor struct {
	originalWriter http.ResponseWriter
	targetCode     int
	headers        http.Header
	gotCode        bool
	intercepted    bool
}

func newErrorInterceptor(w http.ResponseWriter, code int) *errorInterceptor {
	i := errorInterceptor{originalWriter: w, targetCode: code}
	i.headers = make(http.Header)
	copyHeaders(w.Header(), i.headers)
	return &i
}

// Header implements http.ResponseWriter
func (i *errorInterceptor) Header() http.Header {
	return i.headers
}

// WriteHeader implements http.ResponseWriter
func (i *errorInterceptor) WriteHeader(code int) {
	if i.gotCode {
		panic("errorInterceptor.WriteHeader() called twice")
	}

	i.gotCode = true
	if code == i.targetCode {
		i.intercepted = true
	} else {
		copyHeaders(i.headers, i.originalWriter.Header())
		i.originalWriter.WriteHeader(code)
	}
}

// Write implements http.ResponseWriter
func (i *errorInterceptor) Write(data []byte) (int, error) {
	if !i.gotCode {
		i.WriteHeader(http.StatusOK)
	}
	if !i.intercepted {
		return i.originalWriter.Write(data)
	}
	return len(data), nil
}
<|endoftext|>


<commit_before>package route

import (
	"fmt"
	"github.com/blang/semver"
	"github.com/cloudfoundry/cli/cf/api"
	"github.com/cloudfoundry/cli/cf/command_registry"
	"github.com/cloudfoundry/cli/cf/configuration/core_config"
	"github.com/cloudfoundry/cli/cf/errors"
	. "github.com/cloudfoundry/cli/cf/i18n"
	"github.com/cloudfoundry/cli/cf/requirements"
	"github.com/cloudfoundry/cli/cf/terminal"
	"github.com/cloudfoundry/cli/flags"
)

type DeleteRoute struct {
	ui        terminal.UI
	config    core_config.Reader
	routeRepo api.RouteRepository
	domainReq requirements.DomainRequirement
}

func init() {
	command_registry.Register(&DeleteRoute{})
}

func (cmd *DeleteRoute) MetaData() command_registry.CommandMetadata {
	fs := make(map[string]flags.FlagSet)
	fs["f"] = &flags.BoolFlag{ShortName: "f", Usage: T("Force deletion without confirmation")}
	fs["hostname"] = &flags.StringFlag{Name: "hostname", ShortName: "n", Usage: T("Hostname used to identify the route")}
	fs["path"] = &flags.StringFlag{Name: "path", Usage: T("Path used to identify the route")}
	fs["port"] = &flags.IntFlag{Name: "port", Usage: T("Port used to identify the TCP route")}

	return command_registry.CommandMetadata{
		Name:        "delete-route",
		Description: T("Delete a route"),
		Usage: []string{
			fmt.Sprintf("%s:\n", T("Delete an HTTP route")),
			"      CF_NAME delete-route ",
			fmt.Sprintf("%s ", T("DOMAIN")),
			fmt.Sprintf("[--hostname %s] ", T("HOSTNAME")),
			fmt.Sprintf("[--path %s] [-f]\n\n", T("PATH")),
			fmt.Sprintf("   %s:\n", T("Delete a TCP route")),
			"      CF_NAME delete-route ",
			fmt.Sprintf("%s ", T("DOMAIN")),
			fmt.Sprintf("--port %s [-f]", T("PORT")),
		},
		Examples: []string{
			"CF_NAME delete-route example.com                              # example.com",
			"CF_NAME delete-route example.com --hostname myhost            # myhost.example.com",
			"CF_NAME delete-route example.com --hostname myhost --path foo # myhost.example.com/foo",
			"CF_NAME delete-route example.com --port 50000                 # example.com:50000",
		},
		Flags: fs,
	}
}

func (cmd *DeleteRoute) Requirements(requirementsFactory requirements.Factory, fc flags.FlagContext) []requirements.Requirement {
	if len(fc.Args()) != 1 {
		cmd.ui.Failed(T("Incorrect Usage. Requires an argument\n\n") + command_registry.Commands.CommandUsage("delete-route"))
	}

	if fc.IsSet("port") && (fc.IsSet("hostname") || fc.IsSet("path")) {
		cmd.ui.Failed(T("Cannot specify port together with hostname and/or path."))
	}

	cmd.domainReq = requirementsFactory.NewDomainRequirement(fc.Args()[0])

	requiredVersion, err := semver.Make("2.36.0")
	if err != nil {
		panic(err.Error())
	}

	var reqs []requirements.Requirement

	if fc.String("path") != "" {
		reqs = append(reqs, requirementsFactory.NewMinAPIVersionRequirement("Option '--path'", requiredVersion))
	}

	reqs = append(reqs, []requirements.Requirement{
		requirementsFactory.NewLoginRequirement(),
		cmd.domainReq,
	}...)

	return reqs
}

func (cmd *DeleteRoute) SetDependency(deps command_registry.Dependency, pluginCall bool) command_registry.Command {
	cmd.ui = deps.Ui
	cmd.config = deps.Config
	cmd.routeRepo = deps.RepoLocator.GetRouteRepository()
	return cmd
}

func (cmd *DeleteRoute) Execute(c flags.FlagContext) {
	var port int

	host := c.String("n")
	path := c.String("path")
	domainName := c.Args()[0]

	url := domainName
	if host != "" {
		url = host + "." + domainName
	}
	if !c.Bool("f") {
		if !cmd.ui.ConfirmDelete("route", url) {
			return
		}
	}

	cmd.ui.Say(T("Deleting route {{.URL}}...", map[string]interface{}{"URL": terminal.EntityNameColor(url)}))

	domain := cmd.domainReq.GetDomain()
	route, err := cmd.routeRepo.Find(host, domain, path, port)
	if err != nil {
		if _, ok := err.(*errors.ModelNotFoundError); ok {
			cmd.ui.Warn(T("Unable to delete, route '{{.URL}}' does not exist.",
				map[string]interface{}{"URL": url}))
			return
		}
		cmd.ui.Failed(err.Error())
		return
	}

	err = cmd.routeRepo.Delete(route.Guid)
	if err != nil {
		cmd.ui.Failed(err.Error())
		return
	}

	cmd.ui.Ok()
}
<commit_msg>Gate port deletion behind min CCAPI version<commit_after>package route

import (
	"fmt"

	"github.com/blang/semver"
	"github.com/cloudfoundry/cli/cf"
	"github.com/cloudfoundry/cli/cf/api"
	"github.com/cloudfoundry/cli/cf/command_registry"
	"github.com/cloudfoundry/cli/cf/configuration/core_config"
	"github.com/cloudfoundry/cli/cf/errors"
	. "github.com/cloudfoundry/cli/cf/i18n"
	"github.com/cloudfoundry/cli/cf/requirements"
	"github.com/cloudfoundry/cli/cf/terminal"
	"github.com/cloudfoundry/cli/flags"
)

type DeleteRoute struct {
	ui        terminal.UI
	config    core_config.Reader
	routeRepo api.RouteRepository
	domainReq requirements.DomainRequirement
}

func init() {
	command_registry.Register(&DeleteRoute{})
}

func (cmd *DeleteRoute) MetaData() command_registry.CommandMetadata {
	fs := make(map[string]flags.FlagSet)
	fs["f"] = &flags.BoolFlag{ShortName: "f", Usage: T("Force deletion without confirmation")}
	fs["hostname"] = &flags.StringFlag{Name: "hostname", ShortName: "n", Usage: T("Hostname used to identify the route")}
	fs["path"] = &flags.StringFlag{Name: "path", Usage: T("Path used to identify the route")}
	fs["port"] = &flags.IntFlag{Name: "port", Usage: T("Port used to identify the TCP route")}

	return command_registry.CommandMetadata{
		Name:        "delete-route",
		Description: T("Delete a route"),
		Usage: []string{
			fmt.Sprintf("%s:\n", T("Delete an HTTP route")),
			"      CF_NAME delete-route ",
			fmt.Sprintf("%s ", T("DOMAIN")),
			fmt.Sprintf("[--hostname %s] ", T("HOSTNAME")),
			fmt.Sprintf("[--path %s] [-f]\n\n", T("PATH")),
			fmt.Sprintf("   %s:\n", T("Delete a TCP route")),
			"      CF_NAME delete-route ",
			fmt.Sprintf("%s ", T("DOMAIN")),
			fmt.Sprintf("--port %s [-f]", T("PORT")),
		},
		Examples: []string{
			"CF_NAME delete-route example.com                              # example.com",
			"CF_NAME delete-route example.com --hostname myhost            # myhost.example.com",
			"CF_NAME delete-route example.com --hostname myhost --path foo # myhost.example.com/foo",
			"CF_NAME delete-route example.com --port 50000                 # example.com:50000",
		},
		Flags: fs,
	}
}

func (cmd *DeleteRoute) Requirements(requirementsFactory requirements.Factory, fc flags.FlagContext) []requirements.Requirement {
	if len(fc.Args()) != 1 {
		cmd.ui.Failed(T("Incorrect Usage. Requires an argument\n\n") + command_registry.Commands.CommandUsage("delete-route"))
	}

	if fc.IsSet("port") && (fc.IsSet("hostname") || fc.IsSet("path")) {
		cmd.ui.Failed(T("Cannot specify port together with hostname and/or path."))
	}

	cmd.domainReq = requirementsFactory.NewDomainRequirement(fc.Args()[0])

	requiredVersion, err := semver.Make("2.36.0")
	if err != nil {
		panic(err.Error())
	}

	var reqs []requirements.Requirement

	if fc.String("path") != "" {
		reqs = append(reqs, requirementsFactory.NewMinAPIVersionRequirement("Option '--path'", requiredVersion))
	}

	if fc.IsSet("port") {
		reqs = append(reqs, requirementsFactory.NewMinAPIVersionRequirement("Option '--port'", cf.TcpRoutingMinimumApiVersion))
	}

	reqs = append(reqs, []requirements.Requirement{
		requirementsFactory.NewLoginRequirement(),
		cmd.domainReq,
	}...)

	return reqs
}

func (cmd *DeleteRoute) SetDependency(deps command_registry.Dependency, pluginCall bool) command_registry.Command {
	cmd.ui = deps.Ui
	cmd.config = deps.Config
	cmd.routeRepo = deps.RepoLocator.GetRouteRepository()
	return cmd
}

func (cmd *DeleteRoute) Execute(c flags.FlagContext) {
	var port int

	host := c.String("n")
	path := c.String("path")
	domainName := c.Args()[0]

	url := domainName
	if host != "" {
		url = host + "." + domainName
	}
	if !c.Bool("f") {
		if !cmd.ui.ConfirmDelete("route", url) {
			return
		}
	}

	cmd.ui.Say(T("Deleting route {{.URL}}...", map[string]interface{}{"URL": terminal.EntityNameColor(url)}))

	domain := cmd.domainReq.GetDomain()
	route, err := cmd.routeRepo.Find(host, domain, path, port)
	if err != nil {
		if _, ok := err.(*errors.ModelNotFoundError); ok {
			cmd.ui.Warn(T("Unable to delete, route '{{.URL}}' does not exist.",
				map[string]interface{}{"URL": url}))
			return
		}
		cmd.ui.Failed(err.Error())
		return
	}

	err = cmd.routeRepo.Delete(route.Guid)
	if err != nil {
		cmd.ui.Failed(err.Error())
		return
	}

	cmd.ui.Ok()
}
<|endoftext|>


<commit_before>// Copyright © 2012, Greg Ward. All rights reserved.
// Use of this source code is governed by a BSD-style license that can
// be found in the LICENSE.txt file.

package runtime

import (
	"fmt"
	"os"
	"os/exec"
	"strings"

	"fubsy/dsl"
	"fubsy/types"
)

type Action interface {
	String() string

	// Perform whatever task this action implies. Return nil on
	// success, error otherwise. Compound actions always fail on the
	// first error; they do not continue executing. (The global
	// "--keep-going" option is irrelevant at this level; the caller
	// of Execute() is responsible for respecting that option.)
	Execute(ns types.Namespace) error
}

type actionbase struct {
}

// an action that just consists of a list of other actions
type SequenceAction struct {
	actionbase
	subactions []Action
}

// an action that is a shell command to execute
type CommandAction struct {
	actionbase

	// as read from the build script, without variables expanded
	raw types.FuObject

	// with all variables expanded, ready to execute
	expanded types.FuObject
}

// an action that evaluates an expression and assigns the result to a
// local variable -- only affects the scope of one build rule
type AssignmentAction struct {
	actionbase
	assignment *dsl.ASTAssignment
}

// an action that calls a function with real-world side effects (e.g.
// remove(), copyfile()) -- a pure function would be useless here,
// since we do nothing with the return value!
type FunctionCallAction struct {
	actionbase
	fcall *dsl.ASTFunctionCall
}

func NewSequenceAction() *SequenceAction {
	result := new(SequenceAction)
	return result
}

func (self *SequenceAction) String() string {
	result := make([]string, len(self.subactions))
	for i, sub := range self.subactions {
		result[i] = sub.String()
	}
	var tail string
	if len(result) > 3 {
		result = result[0:3]
		tail = " && ..."
	}
	return strings.Join(result, " && ") + tail
}

func (self *SequenceAction) Execute(ns types.Namespace) error {
	var err error
	for _, sub := range self.subactions {
		err = sub.Execute(ns)
		if err != nil {
			return err
		}
	}
	return nil
}

func (self *SequenceAction) AddAction(action Action) {
	self.subactions = append(self.subactions, action)
}

func (self *SequenceAction) AddCommand(command *dsl.ASTString) {
	raw := types.FuString(command.Value())
	self.AddAction(&CommandAction{raw: raw})
}

func (self *SequenceAction) AddAssignment(assignment *dsl.ASTAssignment) {
	self.AddAction(&AssignmentAction{assignment: assignment})
}

func (self *SequenceAction) AddFunctionCall(fcall *dsl.ASTFunctionCall) {
	self.AddAction(&FunctionCallAction{fcall: fcall})
}

func (self *CommandAction) String() string {
	return self.raw.String()
}

func (self *CommandAction) Execute(ns types.Namespace) error {
	//fmt.Println(self.raw)

	var err error
	self.expanded, err = self.raw.Expand(ns)
	if err != nil {
		return err
	}
	fmt.Println(self.expanded)

	// Run commands with the shell because people expect redirection,
	// pipes, etc. to work from their build scripts. (And besides, all
	// we have is a string: Fubsy makes no effort to encourage
	// commands as lists. That just confuses people and causes excess
	// typing. And it's pointless on Windows, where command lists get
	// collapsed to a string and then parsed back into words by the
	// program being run.)
	// XXX can we mitigate security risks of using the shell?
	// XXX what about Windows?
	// XXX for parallel builds: gather stdout and stderr, accumulate
	// them in order but still distinguishing them, and dump them to
	// our stdout/stderr when the command finishes
	// XXX the error message doesn't say which command failed (and if
	// it did, it would probably say "/bin/sh", which is useless): can
	// we do better?
	cmd := exec.Command("/bin/sh", "-c", self.expanded.String())
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	err = cmd.Run()
	return err
}

func (self *AssignmentAction) String() string {
	return self.assignment.Target() + " = ..."
	//return self.assignment.String()
}

func (self *AssignmentAction) Execute(ns types.Namespace) error {
	panic("assignment in build rule not implemented yet")
}

func (self *FunctionCallAction) String() string {
	return self.fcall.String() + "(...)"
}

func (self *FunctionCallAction) Execute(ns types.Namespace) error {
	panic("function call in build rule not implemeneted yet")
}
<commit_msg>runtime: implement local variable assignment in a build rule<commit_after>// Copyright © 2012, Greg Ward. All rights reserved.
// Use of this source code is governed by a BSD-style license that can
// be found in the LICENSE.txt file.

package runtime

import (
	"fmt"
	"os"
	"os/exec"
	"strings"

	"fubsy/dsl"
	"fubsy/types"
)

type Action interface {
	String() string

	// Perform whatever task this action implies. Return nil on
	// success, error otherwise. Compound actions always fail on the
	// first error; they do not continue executing. (The global
	// "--keep-going" option is irrelevant at this level; the caller
	// of Execute() is responsible for respecting that option.)
	Execute(ns types.Namespace) error
}

type actionbase struct {
}

// an action that just consists of a list of other actions
type SequenceAction struct {
	actionbase
	subactions []Action
}

// an action that is a shell command to execute
type CommandAction struct {
	actionbase

	// as read from the build script, without variables expanded
	raw types.FuObject

	// with all variables expanded, ready to execute
	expanded types.FuObject
}

// an action that evaluates an expression and assigns the result to a
// local variable -- only affects the scope of one build rule
type AssignmentAction struct {
	actionbase
	assignment *dsl.ASTAssignment
}

// an action that calls a function with real-world side effects (e.g.
// remove(), copyfile()) -- a pure function would be useless here,
// since we do nothing with the return value!
type FunctionCallAction struct {
	actionbase
	fcall *dsl.ASTFunctionCall
}

func NewSequenceAction() *SequenceAction {
	result := new(SequenceAction)
	return result
}

func (self *SequenceAction) String() string {
	result := make([]string, len(self.subactions))
	for i, sub := range self.subactions {
		result[i] = sub.String()
	}
	var tail string
	if len(result) > 3 {
		result = result[0:3]
		tail = " && ..."
	}
	return strings.Join(result, " && ") + tail
}

func (self *SequenceAction) Execute(ns types.Namespace) error {
	var err error
	for _, sub := range self.subactions {
		err = sub.Execute(ns)
		if err != nil {
			return err
		}
	}
	return nil
}

func (self *SequenceAction) AddAction(action Action) {
	self.subactions = append(self.subactions, action)
}

func (self *SequenceAction) AddCommand(command *dsl.ASTString) {
	raw := types.FuString(command.Value())
	self.AddAction(&CommandAction{raw: raw})
}

func (self *SequenceAction) AddAssignment(assignment *dsl.ASTAssignment) {
	self.AddAction(&AssignmentAction{assignment: assignment})
}

func (self *SequenceAction) AddFunctionCall(fcall *dsl.ASTFunctionCall) {
	self.AddAction(&FunctionCallAction{fcall: fcall})
}

func (self *CommandAction) String() string {
	return self.raw.String()
}

func (self *CommandAction) Execute(ns types.Namespace) error {
	//fmt.Println(self.raw)

	var err error
	self.expanded, err = self.raw.Expand(ns)
	if err != nil {
		return err
	}
	fmt.Println(self.expanded)

	// Run commands with the shell because people expect redirection,
	// pipes, etc. to work from their build scripts. (And besides, all
	// we have is a string: Fubsy makes no effort to encourage
	// commands as lists. That just confuses people and causes excess
	// typing. And it's pointless on Windows, where command lists get
	// collapsed to a string and then parsed back into words by the
	// program being run.)
	// XXX can we mitigate security risks of using the shell?
	// XXX what about Windows?
	// XXX for parallel builds: gather stdout and stderr, accumulate
	// them in order but still distinguishing them, and dump them to
	// our stdout/stderr when the command finishes
	// XXX the error message doesn't say which command failed (and if
	// it did, it would probably say "/bin/sh", which is useless): can
	// we do better?
	cmd := exec.Command("/bin/sh", "-c", self.expanded.String())
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	err = cmd.Run()
	return err
}

func (self *AssignmentAction) String() string {
	return self.assignment.Target() + " = ..."
	//return self.assignment.String()
}

func (self *AssignmentAction) Execute(ns types.Namespace) error {
	return assign(ns, self.assignment)
}

func (self *FunctionCallAction) String() string {
	return self.fcall.String() + "(...)"
}

func (self *FunctionCallAction) Execute(ns types.Namespace) error {
	panic("function call in build rule not implemented yet")
}
<|endoftext|>


<commit_before>package cache

import (
	"bytes"
	"github.com/phzfi/RIC/server/testutils"
	"testing"
	"time"
)

const cachefolder = "/tmp/cachetests"

func TestMemCache(t *testing.T) {
	allTests(t, setupMemcache)
}

func TestDiskCache(t *testing.T) {
	allTests(t, func() (*DummyPolicy, *Cache) {
		testutils.RemoveContents(cachefolder)
		return setupDiskCache()
	})
}

func TestDiskCachePersistence(t *testing.T) {
	id := string("testdiskpersist")
	data := []byte{1, 2, 3, 4, 7}

	_, cache := setupDiskCache()
	cache.AddBlob(id, data)

	time.Sleep(100)

	_, cache = setupDiskCache()
	recovered, ok := cache.GetBlob(id)

	if !ok {
		t.Fatal("The new cache instance did not find the image previously saved on disk.")
	}

	if !bytes.Equal(data, recovered) {
		t.Fatal("The cache returned different data than what was cached.")
	}
}

func setupDiskCache() (dp *DummyPolicy, cache *Cache) {
	dp = NewDummyPolicy(make(Log))
	cache = NewDiskCache(cachefolder, 100, dp)
	return
}

func allTests(t *testing.T, f setupFunc) {
	testCache(t, f)
	testCacheExit(t, f)
}

const (
	Visit = iota
	Push
	Pop
)

type Log map[string][]uint

type DummyPolicy struct {
	fifo Policy

	loki Log
	pops int
}

func (d DummyPolicy) Visit(k string) {
	d.log(k, Visit)
	d.fifo.Visit(k)
}

func (d DummyPolicy) log(k string, t uint) {
	d.loki[k] = append(d.loki[k], t)
}

func (d DummyPolicy) Push(k string) {
	d.log(k, Push)
	d.fifo.Push(k)
}

func (d *DummyPolicy) Pop() string {
	d.pops += 1
	return d.fifo.Pop()
}

func NewDummyPolicy(log Log) *DummyPolicy {
	return &DummyPolicy{fifo: &FIFO{}, loki: log}
}

func setupMemcache() (dp *DummyPolicy, cache *Cache) {
	dp = NewDummyPolicy(make(Log))
	cache = NewCache(dp, 100)
	return
}

type setupFunc func() (dp *DummyPolicy, cache *Cache)

func testCache(t *testing.T, setup setupFunc) {
	id := string("testcache")
	dp, cache := setup()

	found := func() bool {
		_, ok := cache.GetBlob(id)
		return ok
	}

	if found() {
		t.Fatal("Cache claimed to contain a blob that was never added")
	}

	cache.AddBlob(id, make([]byte, 10))

	time.Sleep(100) // only necessary for pure disk cache

	if tx := dp.loki[id]; len(tx) != 1 || tx[0] != Push {
		t.Fatal("Cache did not use policy properly")
	}

	if !found() {
		t.Fatal("Not found after adding to cache")
	}

	if tx := dp.loki[id]; len(tx) != 2 || tx[1] != Visit {
		t.Fatal("Cache did not use policy properly")
	}
}

func testCacheExit(t *testing.T, setup setupFunc) {
	var (
		id1 = string("cacheexit1")
		id2 = string("cacheexit2")
		id3 = string("cacheexit3")
	)
	dp, cache := setup()

	cache.AddBlob(id1, make([]byte, 50))
	cache.AddBlob(id2, make([]byte, 40))
	cache.AddBlob(id3, make([]byte, 20))

	if dp.pops != 1 {
		t.Fatal("Wrong amount of blobs removed from cache")
	}
}
<commit_msg>wait for 100ms, not ns<commit_after>package cache

import (
	"bytes"
	"github.com/phzfi/RIC/server/testutils"
	"testing"
	"time"
)

const cachefolder = "/tmp/cachetests"

func TestMemCache(t *testing.T) {
	allTests(t, setupMemcache)
}

func TestDiskCache(t *testing.T) {
	allTests(t, func() (*DummyPolicy, *Cache) {
		testutils.RemoveContents(cachefolder)
		return setupDiskCache()
	})
}

func TestDiskCachePersistence(t *testing.T) {
	id := string("testdiskpersist")
	data := []byte{1, 2, 3, 4, 7}

	_, cache := setupDiskCache()
	cache.AddBlob(id, data)

	time.Sleep(100)

	_, cache = setupDiskCache()
	recovered, ok := cache.GetBlob(id)

	if !ok {
		t.Fatal("The new cache instance did not find the image previously saved on disk.")
	}

	if !bytes.Equal(data, recovered) {
		t.Fatal("The cache returned different data than what was cached.")
	}
}

func setupDiskCache() (dp *DummyPolicy, cache *Cache) {
	dp = NewDummyPolicy(make(Log))
	cache = NewDiskCache(cachefolder, 100, dp)
	return
}

func allTests(t *testing.T, f setupFunc) {
	testCache(t, f)
	testCacheExit(t, f)
}

const (
	Visit = iota
	Push
	Pop
)

type Log map[string][]uint

type DummyPolicy struct {
	fifo Policy

	loki Log
	pops int
}

func (d DummyPolicy) Visit(k string) {
	d.log(k, Visit)
	d.fifo.Visit(k)
}

func (d DummyPolicy) log(k string, t uint) {
	d.loki[k] = append(d.loki[k], t)
}

func (d DummyPolicy) Push(k string) {
	d.log(k, Push)
	d.fifo.Push(k)
}

func (d *DummyPolicy) Pop() string {
	d.pops += 1
	return d.fifo.Pop()
}

func NewDummyPolicy(log Log) *DummyPolicy {
	return &DummyPolicy{fifo: &FIFO{}, loki: log}
}

func setupMemcache() (dp *DummyPolicy, cache *Cache) {
	dp = NewDummyPolicy(make(Log))
	cache = NewCache(dp, 100)
	return
}

type setupFunc func() (dp *DummyPolicy, cache *Cache)

func testCache(t *testing.T, setup setupFunc) {
	id := string("testcache")
	dp, cache := setup()

	found := func() bool {
		_, ok := cache.GetBlob(id)
		return ok
	}

	if found() {
		t.Fatal("Cache claimed to contain a blob that was never added")
	}

	cache.AddBlob(id, make([]byte, 10))

	time.Sleep(100 * time.Millisecond) // only necessary for pure disk cache

	if tx := dp.loki[id]; len(tx) != 1 || tx[0] != Push {
		t.Fatal("Cache did not use policy properly")
	}

	if !found() {
		t.Fatal("Not found after adding to cache")
	}

	if tx := dp.loki[id]; len(tx) != 2 || tx[1] != Visit {
		t.Fatal("Cache did not use policy properly")
	}
}

func testCacheExit(t *testing.T, setup setupFunc) {
	var (
		id1 = string("cacheexit1")
		id2 = string("cacheexit2")
		id3 = string("cacheexit3")
	)
	dp, cache := setup()

	cache.AddBlob(id1, make([]byte, 50))
	cache.AddBlob(id2, make([]byte, 40))
	cache.AddBlob(id3, make([]byte, 20))

	if dp.pops != 1 {
		t.Fatal("Wrong amount of blobs removed from cache")
	}
}
<|endoftext|>