<commit_before>package manifests

import (
	"encoding/base64"
	"path/filepath"

	"github.com/aws/aws-sdk-go/aws/session"

	// TODO(flaper87): Migrate to ghodss asap
	// This yaml is currently used only by the OpenStack
	// clouds serialization. We're working on migrating
	// clientconfig out of go-yaml. We'll use it here
	// until that happens.
	// https://github.com/openshift/installer/pull/854
	"gopkg.in/yaml.v2"

	"github.com/gophercloud/utils/openstack/clientconfig"
	"github.com/openshift/installer/pkg/asset"
	"github.com/openshift/installer/pkg/asset/installconfig"
	"github.com/openshift/installer/pkg/asset/machines"
	"github.com/openshift/installer/pkg/asset/password"
	"github.com/openshift/installer/pkg/asset/templates/content/openshift"
)

const (
	openshiftManifestDir = "openshift"
)

var (
	_ asset.WritableAsset = (*Openshift)(nil)
)

// Openshift generates the dependent resource manifests for openShift (as against bootkube)
type Openshift struct {
	FileList []*asset.File
}

// Name returns a human friendly name for the operator
func (o *Openshift) Name() string {
	return "Openshift Manifests"
}

// Dependencies returns all of the dependencies directly needed by the
// Openshift asset
func (o *Openshift) Dependencies() []asset.Asset {
	return []asset.Asset{
		&installconfig.InstallConfig{},
		&ClusterK8sIO{},
		&machines.Worker{},
		&machines.Master{},
		&password.KubeadminPassword{},

		&openshift.BindingDiscovery{},
		&openshift.CloudCredsSecret{},
		&openshift.KubeadminPasswordSecret{},
		&openshift.RoleCloudCredsSecretReader{},
	}
}

// Generate generates the respective operator config.yml files
func (o *Openshift) Generate(dependencies asset.Parents) error {
	installConfig := &installconfig.InstallConfig{}
	kubeadminPassword := &password.KubeadminPassword{}
	clusterk8sio := &ClusterK8sIO{}
	worker := &machines.Worker{}
	master := &machines.Master{}
	dependencies.Get(installConfig, clusterk8sio, worker, master, kubeadminPassword)
	var cloudCreds cloudCredsSecretData
	platform := installConfig.Config.Platform.Name()
	switch platform {
	case "aws":
		ssn := session.Must(session.NewSessionWithOptions(session.Options{
			SharedConfigState: session.SharedConfigEnable,
		}))
		creds, err := ssn.Config.Credentials.Get()
		if err != nil {
			return err
		}
		cloudCreds = cloudCredsSecretData{
			AWS: &AwsCredsSecretData{
				Base64encodeAccessKeyID:     base64.StdEncoding.EncodeToString([]byte(creds.AccessKeyID)),
				Base64encodeSecretAccessKey: base64.StdEncoding.EncodeToString([]byte(creds.SecretAccessKey)),
			},
		}
	case "openstack":
		opts := new(clientconfig.ClientOpts)
		cloud, err := clientconfig.GetCloudFromYAML(opts)
		if err != nil {
			return err
		}
		clouds := make(map[string]map[string]*clientconfig.Cloud)
		clouds["clouds"] = map[string]*clientconfig.Cloud{
			"openstack": cloud,
		}

		marshalled, err := yaml.Marshal(clouds)
		if err != nil {
			return err
		}

		credsEncoded := base64.StdEncoding.EncodeToString(marshalled)
		cloudCreds = cloudCredsSecretData{
			OpenStack: &OpenStackCredsSecretData{
				Base64encodeCloudCreds: credsEncoded,
			},
		}
	}

	templateData := &openshiftTemplateData{
		CloudCreds:                   cloudCreds,
		Base64EncodedKubeadminPwHash: base64.StdEncoding.EncodeToString(kubeadminPassword.PasswordHash),
	}

	bindingDiscovery := &openshift.BindingDiscovery{}
	cloudCredsSecret := &openshift.CloudCredsSecret{}
	kubeadminPasswordSecret := &openshift.KubeadminPasswordSecret{}
	roleCloudCredsSecretReader := &openshift.RoleCloudCredsSecretReader{}
	dependencies.Get(
		bindingDiscovery,
		cloudCredsSecret,
		kubeadminPasswordSecret,
		roleCloudCredsSecretReader)
	assetData := map[string][]byte{
		"99_binding-discovery.yaml":                             []byte(bindingDiscovery.Files()[0].Data),
		"99_kubeadmin-password-secret.yaml":                     applyTemplateData(kubeadminPasswordSecret.Files()[0].Data, templateData),
		"99_openshift-cluster-api_cluster.yaml":                 clusterk8sio.Raw,
		"99_openshift-cluster-api_master-machines.yaml":         master.MachinesRaw,
		"99_openshift-cluster-api_master-user-data-secret.yaml": master.UserDataSecretRaw,
		"99_openshift-cluster-api_worker-machineset.yaml":       worker.MachineSetRaw,
		"99_openshift-cluster-api_worker-user-data-secret.yaml": worker.UserDataSecretRaw,
	}

	switch platform {
	case "aws", "openstack":
		assetData["99_cloud-creds-secret.yaml"] = applyTemplateData(cloudCredsSecret.Files()[0].Data, templateData)
		assetData["99_role-cloud-creds-secret-reader.yaml"] = applyTemplateData(roleCloudCredsSecretReader.Files()[0].Data, templateData)
	}

	o.FileList = []*asset.File{}
	for name, data := range assetData {
		o.FileList = append(o.FileList, &asset.File{
			Filename: filepath.Join(openshiftManifestDir, name),
			Data:     data,
		})
	}

	return nil
}

// Files returns the files generated by the asset.
func (o *Openshift) Files() []*asset.File {
	return o.FileList
}

// Load returns the openshift asset from disk.
func (o *Openshift) Load(f asset.FileFetcher) (bool, error) {
	fileList, err := f.FetchByPattern(filepath.Join(openshiftManifestDir, "*"))
	if err != nil {
		return false, err
	}
	o.FileList = fileList
	return len(fileList) > 0, nil
}
<commit_msg>openstack: Always set opts.Cloud<commit_after>package manifests

import (
	"encoding/base64"
	"path/filepath"

	"github.com/aws/aws-sdk-go/aws/session"

	// TODO(flaper87): Migrate to ghodss asap
	// This yaml is currently used only by the OpenStack
	// clouds serialization. We're working on migrating
	// clientconfig out of go-yaml. We'll use it here
	// until that happens.
	// https://github.com/openshift/installer/pull/854
	"gopkg.in/yaml.v2"

	"github.com/gophercloud/utils/openstack/clientconfig"
	"github.com/openshift/installer/pkg/asset"
	"github.com/openshift/installer/pkg/asset/installconfig"
	"github.com/openshift/installer/pkg/asset/machines"
	"github.com/openshift/installer/pkg/asset/password"
	"github.com/openshift/installer/pkg/asset/templates/content/openshift"
)

const (
	openshiftManifestDir = "openshift"
)

var (
	_ asset.WritableAsset = (*Openshift)(nil)
)

// Openshift generates the dependent resource manifests for openShift (as against bootkube)
type Openshift struct {
	FileList []*asset.File
}

// Name returns a human friendly name for the operator
func (o *Openshift) Name() string {
	return "Openshift Manifests"
}

// Dependencies returns all of the dependencies directly needed by the
// Openshift asset
func (o *Openshift) Dependencies() []asset.Asset {
	return []asset.Asset{
		&installconfig.InstallConfig{},
		&ClusterK8sIO{},
		&machines.Worker{},
		&machines.Master{},
		&password.KubeadminPassword{},

		&openshift.BindingDiscovery{},
		&openshift.CloudCredsSecret{},
		&openshift.KubeadminPasswordSecret{},
		&openshift.RoleCloudCredsSecretReader{},
	}
}

// Generate generates the respective operator config.yml files
func (o *Openshift) Generate(dependencies asset.Parents) error {
	installConfig := &installconfig.InstallConfig{}
	kubeadminPassword := &password.KubeadminPassword{}
	clusterk8sio := &ClusterK8sIO{}
	worker := &machines.Worker{}
	master := &machines.Master{}
	dependencies.Get(installConfig, clusterk8sio, worker, master, kubeadminPassword)
	var cloudCreds cloudCredsSecretData
	platform := installConfig.Config.Platform.Name()
	switch platform {
	case "aws":
		ssn := session.Must(session.NewSessionWithOptions(session.Options{
			SharedConfigState: session.SharedConfigEnable,
		}))
		creds, err := ssn.Config.Credentials.Get()
		if err != nil {
			return err
		}
		cloudCreds = cloudCredsSecretData{
			AWS: &AwsCredsSecretData{
				Base64encodeAccessKeyID:     base64.StdEncoding.EncodeToString([]byte(creds.AccessKeyID)),
				Base64encodeSecretAccessKey: base64.StdEncoding.EncodeToString([]byte(creds.SecretAccessKey)),
			},
		}
	case "openstack":
		opts := new(clientconfig.ClientOpts)
		opts.Cloud = installConfig.Config.Platform.OpenStack.Cloud
		cloud, err := clientconfig.GetCloudFromYAML(opts)
		if err != nil {
			return err
		}
		clouds := make(map[string]map[string]*clientconfig.Cloud)
		clouds["clouds"] = map[string]*clientconfig.Cloud{
			"openstack": cloud,
		}

		marshalled, err := yaml.Marshal(clouds)
		if err != nil {
			return err
		}

		credsEncoded := base64.StdEncoding.EncodeToString(marshalled)
		cloudCreds = cloudCredsSecretData{
			OpenStack: &OpenStackCredsSecretData{
				Base64encodeCloudCreds: credsEncoded,
			},
		}
	}

	templateData := &openshiftTemplateData{
		CloudCreds:                   cloudCreds,
		Base64EncodedKubeadminPwHash: base64.StdEncoding.EncodeToString(kubeadminPassword.PasswordHash),
	}

	bindingDiscovery := &openshift.BindingDiscovery{}
	cloudCredsSecret := &openshift.CloudCredsSecret{}
	kubeadminPasswordSecret := &openshift.KubeadminPasswordSecret{}
	roleCloudCredsSecretReader := &openshift.RoleCloudCredsSecretReader{}
	dependencies.Get(
		bindingDiscovery,
		cloudCredsSecret,
		kubeadminPasswordSecret,
		roleCloudCredsSecretReader)
	assetData := map[string][]byte{
		"99_binding-discovery.yaml":                             []byte(bindingDiscovery.Files()[0].Data),
		"99_kubeadmin-password-secret.yaml":                     applyTemplateData(kubeadminPasswordSecret.Files()[0].Data, templateData),
		"99_openshift-cluster-api_cluster.yaml":                 clusterk8sio.Raw,
		"99_openshift-cluster-api_master-machines.yaml":         master.MachinesRaw,
		"99_openshift-cluster-api_master-user-data-secret.yaml": master.UserDataSecretRaw,
		"99_openshift-cluster-api_worker-machineset.yaml":       worker.MachineSetRaw,
		"99_openshift-cluster-api_worker-user-data-secret.yaml": worker.UserDataSecretRaw,
	}

	switch platform {
	case "aws", "openstack":
		assetData["99_cloud-creds-secret.yaml"] = applyTemplateData(cloudCredsSecret.Files()[0].Data, templateData)
		assetData["99_role-cloud-creds-secret-reader.yaml"] = applyTemplateData(roleCloudCredsSecretReader.Files()[0].Data, templateData)
	}

	o.FileList = []*asset.File{}
	for name, data := range assetData {
		o.FileList = append(o.FileList, &asset.File{
			Filename: filepath.Join(openshiftManifestDir, name),
			Data:     data,
		})
	}

	return nil
}

// Files returns the files generated by the asset.
func (o *Openshift) Files() []*asset.File {
	return o.FileList
}

// Load returns the openshift asset from disk.
func (o *Openshift) Load(f asset.FileFetcher) (bool, error) {
	fileList, err := f.FetchByPattern(filepath.Join(openshiftManifestDir, "*"))
	if err != nil {
		return false, err
	}
	o.FileList = fileList
	return len(fileList) > 0, nil
}
<|endoftext|>


<commit_before>package token

import (
	"net/http"
	"net/url"
	"strings"

	"github.com/plimble/clover/oauth2"
	"go.uber.org/zap"
)

type ProcessFunc func(req *TokenHandlerRequest) (TokenHandlerResponse, error)

type TokenHandlerRequest struct {
	ClientID     string
	ClientSecret string
	GrantType    string
	Form         url.Values
}

type TokenHandlerResponse map[string]interface{}

type TokenHandler struct {
	GrantTypes     map[string]GrantType
	Storage        oauth2.Storage
	TokenGenerator oauth2.TokenGenerator
	*zap.Logger
}

func (h *TokenHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		oauth2.WriteJsonError(w, ErrNotPostMethod)
		return
	}

	auth := r.Header.Get("Authorization")
	clientID, clientSecret, err := oauth2.GetCredentialsFromHttp(auth)
	if err != nil {
		oauth2.WriteJsonError(w, err)
		return
	}

	if err := r.ParseForm(); err != nil {
		oauth2.WriteJsonError(w, ErrParseForm)
		return
	}

	req := &TokenHandlerRequest{
		ClientID:     clientID,
		ClientSecret: clientSecret,
		GrantType:    r.Form.Get("grant_type"),
		Form:         r.Form,
	}

	res, err := h.RequestToken(req)
	h.Logger.Info("RequestToken",
		zap.Any("TokenHandlerRequest", req),
		zap.Any("TokenHandlerResponse", res),
		zap.Error(err),
	)
	if err != nil {
		oauth2.WriteJsonError(w, err)
		return
	}

	oauth2.WriteJson(w, 200, res)
}

func (h *TokenHandler) RequestToken(req *TokenHandlerRequest) (TokenHandlerResponse, error) {
	grant, err := h.getGrant(req)
	if err != nil {
		return nil, err
	}

	client, err := h.getClient(req)
	if err != nil {
		return nil, err
	}

	grantData, err := grant.GrantRequest(req, client, h.Storage)
	if err != nil {
		return nil, err
	}

	if err = h.checkScope(req.GrantType, grantData.Scopes, client.Scopes); err != nil {
		return nil, err
	}

	accessToken, refreshToken, err := grant.CreateToken(grantData, client, h.Storage, h.TokenGenerator)

	return h.createResponse(grantData, accessToken, refreshToken), err
}

func (h *TokenHandler) getGrant(req *TokenHandlerRequest) (GrantType, error) {
	if req.GrantType == "" {
		return nil, ErrGrantTypeRequired
	}

	grant, ok := h.GrantTypes[req.GrantType]
	if !ok {
		return nil, ErrGrantTypeUnSupported
	}

	return grant, nil
}

func (h *TokenHandler) getClient(req *TokenHandlerRequest) (*oauth2.Client, error) {
	client, err := h.Storage.GetClientWithSecret(req.ClientID, req.ClientSecret)
	if err != nil {
		err = ErrInvalidClient.WithCause(err)
		h.Error("unable to get client",
			zap.String("client_id", client.ID),
			zap.String("client_secret", client.Secret),
			zap.Any("error", err),
		)

		return nil, err
	}

	if !client.IsGrantType(req.GrantType) {
		return nil, ErrGrantIsNotAllowed
	}

	return client, nil
}

func (h *TokenHandler) checkScope(grantName string, grantScopes, clientScopes []string) error {
	var err error

	switch grantName {
	case "client_credentials":
	case "authorization_code":
	case "refresh_token":
		return nil
	}

	ok, err := h.Storage.IsAvailableScope(grantScopes)
	if err != nil {
		err = ErrUnableCheckScope.WithCause(err)
		h.Error("unable get check scope from db",
			zap.Strings("scopes", grantScopes),
			zap.Any("error", err),
		)

		return err
	}
	if !ok {
		return ErrScopeUnSupported
	}

	for _, scope := range grantScopes {
		if !oauth2.HierarchicScope(scope, clientScopes) {
			return ErrScopeNotAllowed
		}
	}

	return nil
}

func (h *TokenHandler) createResponse(grantData *GrantData, accessToken, refreshToken string) TokenHandlerResponse {
	if accessToken == "" {
		return nil
	}

	data := TokenHandlerResponse{
		"access_token": accessToken,
		"token_type":   "bearer",
		"expires_in":   grantData.AccessTokenLifespan,
		"scope":        strings.Join(grantData.Scopes, " "),
	}

	if grantData.UserID != "" {
		data["user_id"] = grantData.UserID
	}

	if refreshToken != "" {
		data["refresh_token"] = refreshToken
	}

	for key, extra := range grantData.Extras {
		data[key] = extra
	}

	return data
}
<commit_msg>add register granttype<commit_after>package token

import (
	"net/http"
	"net/url"
	"strings"

	"github.com/plimble/clover/oauth2"
	"go.uber.org/zap"
)

type ProcessFunc func(req *TokenHandlerRequest) (TokenHandlerResponse, error)

type TokenHandlerRequest struct {
	ClientID     string
	ClientSecret string
	GrantType    string
	Form         url.Values
}

type TokenHandlerResponse map[string]interface{}

type TokenHandler struct {
	GrantTypes     map[string]GrantType
	Storage        oauth2.Storage
	TokenGenerator oauth2.TokenGenerator
	*zap.Logger
}

func (h *TokenHandler) RegisterGrantType(grantType GrantType) {
	h.GrantTypes[grantType.Name()] = grantType
}

func (h *TokenHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		oauth2.WriteJsonError(w, ErrNotPostMethod)
		return
	}

	auth := r.Header.Get("Authorization")
	clientID, clientSecret, err := oauth2.GetCredentialsFromHttp(auth)
	if err != nil {
		oauth2.WriteJsonError(w, err)
		return
	}

	if err := r.ParseForm(); err != nil {
		oauth2.WriteJsonError(w, ErrParseForm)
		return
	}

	req := &TokenHandlerRequest{
		ClientID:     clientID,
		ClientSecret: clientSecret,
		GrantType:    r.Form.Get("grant_type"),
		Form:         r.Form,
	}

	res, err := h.RequestToken(req)
	h.Logger.Info("RequestToken",
		zap.Any("TokenHandlerRequest", req),
		zap.Any("TokenHandlerResponse", res),
		zap.Error(err),
	)
	if err != nil {
		oauth2.WriteJsonError(w, err)
		return
	}

	oauth2.WriteJson(w, 200, res)
}

func (h *TokenHandler) RequestToken(req *TokenHandlerRequest) (TokenHandlerResponse, error) {
	grant, err := h.getGrant(req)
	if err != nil {
		return nil, err
	}

	client, err := h.getClient(req)
	if err != nil {
		return nil, err
	}

	grantData, err := grant.GrantRequest(req, client, h.Storage)
	if err != nil {
		return nil, err
	}

	if err = h.checkScope(req.GrantType, grantData.Scopes, client.Scopes); err != nil {
		return nil, err
	}

	accessToken, refreshToken, err := grant.CreateToken(grantData, client, h.Storage, h.TokenGenerator)

	return h.createResponse(grantData, accessToken, refreshToken), err
}

func (h *TokenHandler) getGrant(req *TokenHandlerRequest) (GrantType, error) {
	if req.GrantType == "" {
		return nil, ErrGrantTypeRequired
	}

	grant, ok := h.GrantTypes[req.GrantType]
	if !ok {
		return nil, ErrGrantTypeUnSupported
	}

	return grant, nil
}

func (h *TokenHandler) getClient(req *TokenHandlerRequest) (*oauth2.Client, error) {
	client, err := h.Storage.GetClientWithSecret(req.ClientID, req.ClientSecret)
	if err != nil {
		err = ErrInvalidClient.WithCause(err)
		h.Error("unable to get client",
			zap.String("client_id", client.ID),
			zap.String("client_secret", client.Secret),
			zap.Any("error", err),
		)

		return nil, err
	}

	if !client.IsGrantType(req.GrantType) {
		return nil, ErrGrantIsNotAllowed
	}

	return client, nil
}

func (h *TokenHandler) checkScope(grantName string, grantScopes, clientScopes []string) error {
	var err error

	switch grantName {
	case "client_credentials":
	case "authorization_code":
	case "refresh_token":
		return nil
	}

	ok, err := h.Storage.IsAvailableScope(grantScopes)
	if err != nil {
		err = ErrUnableCheckScope.WithCause(err)
		h.Error("unable get check scope from db",
			zap.Strings("scopes", grantScopes),
			zap.Any("error", err),
		)

		return err
	}
	if !ok {
		return ErrScopeUnSupported
	}

	for _, scope := range grantScopes {
		if !oauth2.HierarchicScope(scope, clientScopes) {
			return ErrScopeNotAllowed
		}
	}

	return nil
}

func (h *TokenHandler) createResponse(grantData *GrantData, accessToken, refreshToken string) TokenHandlerResponse {
	if accessToken == "" {
		return nil
	}

	data := TokenHandlerResponse{
		"access_token": accessToken,
		"token_type":   "bearer",
		"expires_in":   grantData.AccessTokenLifespan,
		"scope":        strings.Join(grantData.Scopes, " "),
	}

	if grantData.UserID != "" {
		data["user_id"] = grantData.UserID
	}

	if refreshToken != "" {
		data["refresh_token"] = refreshToken
	}

	for key, extra := range grantData.Extras {
		data[key] = extra
	}

	return data
}
<|endoftext|>


<commit_before>/*
Copyright 2014 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 util

import (
	"fmt"
	"io"
	"strings"

	"k8s.io/apimachinery/pkg/api/meta"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/kubernetes/pkg/kubectl"
	"k8s.io/kubernetes/pkg/kubectl/resource"
	"k8s.io/kubernetes/pkg/printers"

	"github.com/spf13/cobra"
)

// AddPrinterFlags adds printing related flags to a command (e.g. output format, no headers, template path)
func AddPrinterFlags(cmd *cobra.Command) {
	AddNonDeprecatedPrinterFlags(cmd)

	cmd.Flags().String("output-version", "", "DEPRECATED: To use a specific API version, fully-qualify the resource, version, and group (for example: 'jobs.v1.batch/myjob').")
	cmd.Flags().MarkDeprecated("output-version", "the resource is used exactly as fetched from the API. To get a specific API version, fully-qualify the resource, version, and group (for example: 'jobs.v1.batch/myjob').")
	cmd.Flags().MarkHidden("output-version")
}

// AddNonDeprecatedPrinterFlags supports the conversion case which must logically have output-version.  Once output-version
// is completely removed, this function can go away.
func AddNonDeprecatedPrinterFlags(cmd *cobra.Command) {
	AddOutputFlags(cmd)
	AddNoHeadersFlags(cmd)
	cmd.Flags().Bool("show-labels", false, "When printing, show all labels as the last column (default hide labels column)")
	cmd.Flags().String("template", "", "Template string or path to template file to use when -o=go-template, -o=go-template-file. The template format is golang templates [http://golang.org/pkg/text/template/#pkg-overview].")
	cmd.MarkFlagFilename("template")
	cmd.Flags().String("sort-by", "", "If non-empty, sort list types using this field specification.  The field specification is expressed as a JSONPath expression (e.g. '{.metadata.name}'). The field in the API resource specified by this JSONPath expression must be an integer or a string.")
	cmd.Flags().BoolP("show-all", "a", false, "When printing, show all resources (default hide terminated pods.)")
}

// AddOutputFlagsForMutation adds output related flags to a command. Used by mutations only.
func AddOutputFlagsForMutation(cmd *cobra.Command) {
	cmd.Flags().StringP("output", "o", "", "Output mode. Use \"-o name\" for shorter output (resource/name).")
}

// AddOutputVarFlagsForMutation adds output related flags to a command. Used by mutations only.
func AddOutputVarFlagsForMutation(cmd *cobra.Command, output *string) {
	cmd.Flags().StringVarP(output, "output", "o", "", "Output mode. Use \"-o name\" for shorter output (resource/name).")
}

// AddOutputFlags adds output related flags to a command.
func AddOutputFlags(cmd *cobra.Command) {
	cmd.Flags().StringP("output", "o", "", "Output format. One of: json|yaml|wide|name|custom-columns=...|custom-columns-file=...|go-template=...|go-template-file=...|jsonpath=...|jsonpath-file=... See custom columns [http://kubernetes.io/docs/user-guide/kubectl-overview/#custom-columns], golang template [http://golang.org/pkg/text/template/#pkg-overview] and jsonpath template [http://kubernetes.io/docs/user-guide/jsonpath].")
	cmd.Flags().Bool("allow-missing-template-keys", true, "If true, ignore any errors in templates when a field or map key is missing in the template. Only applies to golang and jsonpath output formats.")
}

// AddNoHeadersFlags adds no-headers flags to a command.
func AddNoHeadersFlags(cmd *cobra.Command) {
	cmd.Flags().Bool("no-headers", false, "When using the default or custom-column output format, don't print headers (default print headers).")
}

// PrintSuccess prints message after finishing mutating operations
func PrintSuccess(mapper meta.RESTMapper, shortOutput bool, out io.Writer, resource string, name string, dryRun bool, operation string) {
	resource, _ = mapper.ResourceSingularizer(resource)
	dryRunMsg := ""
	if dryRun {
		dryRunMsg = " (dry run)"
	}
	if shortOutput {
		// -o name: prints resource/name
		if len(resource) > 0 {
			fmt.Fprintf(out, "%s/%s\n", resource, name)
		} else {
			fmt.Fprintf(out, "%s\n", name)
		}
	} else {
		// understandable output by default
		if len(resource) > 0 {
			fmt.Fprintf(out, "%s \"%s\" %s%s\n", resource, name, operation, dryRunMsg)
		} else {
			fmt.Fprintf(out, "\"%s\" %s%s\n", name, operation, dryRunMsg)
		}
	}
}

// ValidateOutputArgs validates -o flag args for mutations
func ValidateOutputArgs(cmd *cobra.Command) error {
	outputMode := GetFlagString(cmd, "output")
	if outputMode != "" && outputMode != "name" {
		return UsageErrorf(cmd, "Unexpected -o output mode: %v. We only support '-o name'.", outputMode)
	}
	return nil
}

// PrinterForCommand returns the printer for the outputOptions (if given) or
// returns the default printer for the command. Requires that printer flags have
// been added to cmd (see AddPrinterFlags).
// TODO: remove the dependency on cmd object
func PrinterForCommand(cmd *cobra.Command, outputOpts *printers.OutputOptions, mapper meta.RESTMapper, typer runtime.ObjectTyper, encoder runtime.Encoder, decoders []runtime.Decoder, options printers.PrintOptions) (printers.ResourcePrinter, error) {

	if outputOpts == nil {
		outputOpts = extractOutputOptions(cmd)
	}

	// this function may be invoked by a command that did not call AddPrinterFlags first, so we need
	// to be safe about how we access the no-headers flag
	noHeaders := false
	if cmd.Flags().Lookup("no-headers") != nil {
		noHeaders = GetFlagBool(cmd, "no-headers")
	}
	printer, err := printers.GetStandardPrinter(outputOpts, noHeaders, mapper, typer, encoder, decoders, options)
	if err != nil {
		return nil, err
	}

	return maybeWrapSortingPrinter(cmd, printer), nil
}

// PrintResourceInfoForCommand receives a *cobra.Command and a *resource.Info and
// attempts to print an info object based on the specified output format. If the
// object passed is non-generic, it attempts to print the object using a HumanReadablePrinter.
// Requires that printer flags have been added to cmd (see AddPrinterFlags).
func PrintResourceInfoForCommand(cmd *cobra.Command, info *resource.Info, f Factory, out io.Writer) error {
	printer, err := f.PrinterForCommand(cmd, false, nil, printers.PrintOptions{})
	if err != nil {
		return err
	}
	if !printer.IsGeneric() {
		printer, err = f.PrinterForMapping(cmd, false, nil, nil, false)
		if err != nil {
			return err
		}
	}
	return printer.PrintObj(info.Object, out)
}

// extractOutputOptions parses printer specific commandline args and returns
// printers.OutputsOptions object.
func extractOutputOptions(cmd *cobra.Command) *printers.OutputOptions {
	flags := cmd.Flags()

	var outputFormat string
	if flags.Lookup("output") != nil {
		outputFormat = GetFlagString(cmd, "output")
	}

	// templates are logically optional for specifying a format.
	// TODO once https://github.com/kubernetes/kubernetes/issues/12668 is fixed, this should fall back to GetFlagString
	var templateFile string
	if flags.Lookup("template") != nil {
		templateFile = GetFlagString(cmd, "template")
	}
	if len(outputFormat) == 0 && len(templateFile) != 0 {
		outputFormat = "template"
	}

	templateFormats := []string{
		"go-template=", "go-template-file=", "jsonpath=", "jsonpath-file=", "custom-columns=", "custom-columns-file=",
	}
	for _, format := range templateFormats {
		if strings.HasPrefix(outputFormat, format) {
			templateFile = outputFormat[len(format):]
			outputFormat = format[:len(format)-1]
		}
	}

	// this function may be invoked by a command that did not call AddPrinterFlags first, so we need
	// to be safe about how we access the allow-missing-template-keys flag
	allowMissingTemplateKeys := false
	if flags.Lookup("allow-missing-template-keys") != nil {
		allowMissingTemplateKeys = GetFlagBool(cmd, "allow-missing-template-keys")
	}

	return &printers.OutputOptions{
		FmtType:          outputFormat,
		FmtArg:           templateFile,
		AllowMissingKeys: allowMissingTemplateKeys,
	}
}

func maybeWrapSortingPrinter(cmd *cobra.Command, printer printers.ResourcePrinter) printers.ResourcePrinter {
	sorting, err := cmd.Flags().GetString("sort-by")
	if err != nil {
		// error can happen on missing flag or bad flag type.  In either case, this command didn't intent to sort
		return printer
	}

	if len(sorting) != 0 {
		return &kubectl.SortingPrinter{
			Delegate:  printer,
			SortField: fmt.Sprintf("{%s}", sorting),
		}
	}
	return printer
}
<commit_msg>make sure that the template param is the right type before using it<commit_after>/*
Copyright 2014 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 util

import (
	"fmt"
	"io"
	"strings"

	"k8s.io/apimachinery/pkg/api/meta"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/kubernetes/pkg/kubectl"
	"k8s.io/kubernetes/pkg/kubectl/resource"
	"k8s.io/kubernetes/pkg/printers"

	"github.com/spf13/cobra"
)

// AddPrinterFlags adds printing related flags to a command (e.g. output format, no headers, template path)
func AddPrinterFlags(cmd *cobra.Command) {
	AddNonDeprecatedPrinterFlags(cmd)

	cmd.Flags().String("output-version", "", "DEPRECATED: To use a specific API version, fully-qualify the resource, version, and group (for example: 'jobs.v1.batch/myjob').")
	cmd.Flags().MarkDeprecated("output-version", "the resource is used exactly as fetched from the API. To get a specific API version, fully-qualify the resource, version, and group (for example: 'jobs.v1.batch/myjob').")
	cmd.Flags().MarkHidden("output-version")
}

// AddNonDeprecatedPrinterFlags supports the conversion case which must logically have output-version.  Once output-version
// is completely removed, this function can go away.
func AddNonDeprecatedPrinterFlags(cmd *cobra.Command) {
	AddOutputFlags(cmd)
	AddNoHeadersFlags(cmd)
	cmd.Flags().Bool("show-labels", false, "When printing, show all labels as the last column (default hide labels column)")
	cmd.Flags().String("template", "", "Template string or path to template file to use when -o=go-template, -o=go-template-file. The template format is golang templates [http://golang.org/pkg/text/template/#pkg-overview].")
	cmd.MarkFlagFilename("template")
	cmd.Flags().String("sort-by", "", "If non-empty, sort list types using this field specification.  The field specification is expressed as a JSONPath expression (e.g. '{.metadata.name}'). The field in the API resource specified by this JSONPath expression must be an integer or a string.")
	cmd.Flags().BoolP("show-all", "a", false, "When printing, show all resources (default hide terminated pods.)")
}

// AddOutputFlagsForMutation adds output related flags to a command. Used by mutations only.
func AddOutputFlagsForMutation(cmd *cobra.Command) {
	cmd.Flags().StringP("output", "o", "", "Output mode. Use \"-o name\" for shorter output (resource/name).")
}

// AddOutputVarFlagsForMutation adds output related flags to a command. Used by mutations only.
func AddOutputVarFlagsForMutation(cmd *cobra.Command, output *string) {
	cmd.Flags().StringVarP(output, "output", "o", "", "Output mode. Use \"-o name\" for shorter output (resource/name).")
}

// AddOutputFlags adds output related flags to a command.
func AddOutputFlags(cmd *cobra.Command) {
	cmd.Flags().StringP("output", "o", "", "Output format. One of: json|yaml|wide|name|custom-columns=...|custom-columns-file=...|go-template=...|go-template-file=...|jsonpath=...|jsonpath-file=... See custom columns [http://kubernetes.io/docs/user-guide/kubectl-overview/#custom-columns], golang template [http://golang.org/pkg/text/template/#pkg-overview] and jsonpath template [http://kubernetes.io/docs/user-guide/jsonpath].")
	cmd.Flags().Bool("allow-missing-template-keys", true, "If true, ignore any errors in templates when a field or map key is missing in the template. Only applies to golang and jsonpath output formats.")
}

// AddNoHeadersFlags adds no-headers flags to a command.
func AddNoHeadersFlags(cmd *cobra.Command) {
	cmd.Flags().Bool("no-headers", false, "When using the default or custom-column output format, don't print headers (default print headers).")
}

// PrintSuccess prints message after finishing mutating operations
func PrintSuccess(mapper meta.RESTMapper, shortOutput bool, out io.Writer, resource string, name string, dryRun bool, operation string) {
	resource, _ = mapper.ResourceSingularizer(resource)
	dryRunMsg := ""
	if dryRun {
		dryRunMsg = " (dry run)"
	}
	if shortOutput {
		// -o name: prints resource/name
		if len(resource) > 0 {
			fmt.Fprintf(out, "%s/%s\n", resource, name)
		} else {
			fmt.Fprintf(out, "%s\n", name)
		}
	} else {
		// understandable output by default
		if len(resource) > 0 {
			fmt.Fprintf(out, "%s \"%s\" %s%s\n", resource, name, operation, dryRunMsg)
		} else {
			fmt.Fprintf(out, "\"%s\" %s%s\n", name, operation, dryRunMsg)
		}
	}
}

// ValidateOutputArgs validates -o flag args for mutations
func ValidateOutputArgs(cmd *cobra.Command) error {
	outputMode := GetFlagString(cmd, "output")
	if outputMode != "" && outputMode != "name" {
		return UsageErrorf(cmd, "Unexpected -o output mode: %v. We only support '-o name'.", outputMode)
	}
	return nil
}

// PrinterForCommand returns the printer for the outputOptions (if given) or
// returns the default printer for the command. Requires that printer flags have
// been added to cmd (see AddPrinterFlags).
// TODO: remove the dependency on cmd object
func PrinterForCommand(cmd *cobra.Command, outputOpts *printers.OutputOptions, mapper meta.RESTMapper, typer runtime.ObjectTyper, encoder runtime.Encoder, decoders []runtime.Decoder, options printers.PrintOptions) (printers.ResourcePrinter, error) {

	if outputOpts == nil {
		outputOpts = extractOutputOptions(cmd)
	}

	// this function may be invoked by a command that did not call AddPrinterFlags first, so we need
	// to be safe about how we access the no-headers flag
	noHeaders := false
	if cmd.Flags().Lookup("no-headers") != nil {
		noHeaders = GetFlagBool(cmd, "no-headers")
	}
	printer, err := printers.GetStandardPrinter(outputOpts, noHeaders, mapper, typer, encoder, decoders, options)
	if err != nil {
		return nil, err
	}

	return maybeWrapSortingPrinter(cmd, printer), nil
}

// PrintResourceInfoForCommand receives a *cobra.Command and a *resource.Info and
// attempts to print an info object based on the specified output format. If the
// object passed is non-generic, it attempts to print the object using a HumanReadablePrinter.
// Requires that printer flags have been added to cmd (see AddPrinterFlags).
func PrintResourceInfoForCommand(cmd *cobra.Command, info *resource.Info, f Factory, out io.Writer) error {
	printer, err := f.PrinterForCommand(cmd, false, nil, printers.PrintOptions{})
	if err != nil {
		return err
	}
	if !printer.IsGeneric() {
		printer, err = f.PrinterForMapping(cmd, false, nil, nil, false)
		if err != nil {
			return err
		}
	}
	return printer.PrintObj(info.Object, out)
}

// extractOutputOptions parses printer specific commandline args and returns
// printers.OutputsOptions object.
func extractOutputOptions(cmd *cobra.Command) *printers.OutputOptions {
	flags := cmd.Flags()

	var outputFormat string
	if flags.Lookup("output") != nil {
		outputFormat = GetFlagString(cmd, "output")
	}

	// templates are logically optional for specifying a format.
	// TODO once https://github.com/kubernetes/kubernetes/issues/12668 is fixed, this should fall back to GetFlagString
	var templateFile string
	if flag := flags.Lookup("template"); flag != nil {
		if flag.Value.Type() == "string" {
			templateFile = GetFlagString(cmd, "template")
		}
	}
	if len(outputFormat) == 0 && len(templateFile) != 0 {
		outputFormat = "template"
	}

	templateFormats := []string{
		"go-template=", "go-template-file=", "jsonpath=", "jsonpath-file=", "custom-columns=", "custom-columns-file=",
	}
	for _, format := range templateFormats {
		if strings.HasPrefix(outputFormat, format) {
			templateFile = outputFormat[len(format):]
			outputFormat = format[:len(format)-1]
		}
	}

	// this function may be invoked by a command that did not call AddPrinterFlags first, so we need
	// to be safe about how we access the allow-missing-template-keys flag
	allowMissingTemplateKeys := false
	if flags.Lookup("allow-missing-template-keys") != nil {
		allowMissingTemplateKeys = GetFlagBool(cmd, "allow-missing-template-keys")
	}

	return &printers.OutputOptions{
		FmtType:          outputFormat,
		FmtArg:           templateFile,
		AllowMissingKeys: allowMissingTemplateKeys,
	}
}

func maybeWrapSortingPrinter(cmd *cobra.Command, printer printers.ResourcePrinter) printers.ResourcePrinter {
	sorting, err := cmd.Flags().GetString("sort-by")
	if err != nil {
		// error can happen on missing flag or bad flag type.  In either case, this command didn't intent to sort
		return printer
	}

	if len(sorting) != 0 {
		return &kubectl.SortingPrinter{
			Delegate:  printer,
			SortField: fmt.Sprintf("{%s}", sorting),
		}
	}
	return printer
}
<|endoftext|>


<commit_before>package network

// Maximum time to wait for the xtables lock when using iptables
const xtableLockWait = "3"

// ipTableRules returns a list of commands to append rules for tapDevice.
// If delete=false, this returns the commands to delete the rules.
func ipTableRules(tapDevice string, ipPrefix string, delete bool) [][]string {
	subnet := ipPrefix + ".0/24"
	gateway := ipPrefix + ".1"
	prefixCommands := func(prefix []string, rules [][]string) [][]string {
		cmds := [][]string{}
		for _, rule := range rules {
			cmds = append(cmds, append(prefix, rule...))
		}
		return cmds
	}

	ruleAction := "-A"
	chainAction := "-N"
	if delete {
		ruleAction = "-D"
		chainAction = "-X"
	}

	// Create/delete custom chains for this tap device
	chains := prefixCommands([]string{"iptables", "-w", xtableLockWait, chainAction}, [][]string{
		{"input_" + tapDevice},
		{"output_" + tapDevice},
		{"fwd_input_" + tapDevice},
		{"fwd_output_" + tapDevice},
	})

	// Rules for jumping to custom chains for this tap device
	rules := prefixCommands([]string{"iptables", "-w", xtableLockWait, ruleAction}, [][]string{
		{"INPUT", "-i", tapDevice, "-j", "input_" + tapDevice},
		{"OUTPUT", "-o", tapDevice, "-j", "output_" + tapDevice},
		{"FORWARD", "-i", tapDevice, "-j", "fwd_input_" + tapDevice},
		{"FORWARD", "-o", tapDevice, "-j", "fwd_output_" + tapDevice},
	})

	// Rules for nat from this subnet
	nat := prefixCommands([]string{"iptables", "-w", xtableLockWait, "-t", "nat", ruleAction}, [][]string{
		{"POSTROUTING", "-o", "eth0", "-s", subnet, "-j", "MASQUERADE"},
	})

	// Rules for filtering INPUT from this tap device
	inputRules := prefixCommands([]string{"iptables", "-w", xtableLockWait, ruleAction, "input_" + tapDevice}, [][]string{
		// Allow requests to meta-data service (from subnet only)
		{"-p", "tcp", "-s", subnet, "-d", metaDataIP, "-m", "tcp", "--dport", "80", "-m", "state", "--state", "NEW,ESTABLISHED", "-j", "ACCEPT"},
		// Allow DNS requests
		{"-p", "tcp", "-s", subnet, "-d", gateway, "-m", "tcp", "--dport", "53", "-m", "state", "--state", "NEW,ESTABLISHED", "-j", "ACCEPT"},
		{"-p", "udp", "-s", subnet, "-d", gateway, "-m", "udp", "--dport", "53", "-m", "state", "--state", "NEW,ESTABLISHED", "-j", "ACCEPT"},
		// Allow DCHP requests
		{"-s", "0.0.0.0", "-d", "255.255.255.255", "-p", "udp", "-m", "udp", "--sport", "68", "--dport", "67", "-j", "ACCEPT"},
		{"-s", subnet, "-d", gateway, "-p", "udp", "-m", "udp", "--sport", "68", "--dport", "67", "-j", "ACCEPT"},
		// Reject all other input (with special case for wrong port on meta-data service)
		{"-s", subnet, "-d", metaDataIP, "-j", "REJECT", "--reject-with", "icmp-port-unreachable"},
		{"-j", "REJECT", "--reject-with", "icmp-host-unreachable"},
	})

	// Rules for filtering OUTPUT to this tap decice
	outputRules := prefixCommands([]string{"iptables", "-w", xtableLockWait, ruleAction, "output_" + tapDevice}, [][]string{
		// Allow meta-data replies (to subnet only)
		{"-p", "tcp", "-s", metaDataIP, "-d", subnet, "-m", "tcp", "--sport", "80", "-m", "state", "--state", "ESTABLISHED", "-j", "ACCEPT"},
		// Allow DNS replies from dnsmasq (to subnet only)
		{"-p", "udp", "-s", gateway, "-d", subnet, "-m", "udp", "--sport", "53", "-m", "state", "--state", "ESTABLISHED", "-j", "ACCEPT"},
		{"-p", "tcp", "-s", gateway, "-d", subnet, "-m", "tcp", "--sport", "53", "-m", "state", "--state", "ESTABLISHED", "-j", "ACCEPT"},
		// Allow DHCP replies
		{"-p", "udp", "-s", gateway, "-m", "udp", "--sport", "67", "--dport", "68", "-j", "ACCEPT"},
		// Reject all other output
		{"-j", "REJECT", "--reject-with", "icmp-net-prohibited"},
	})

	// Rules for filtering FORWARD from this tap device
	forwardInputRules := prefixCommands([]string{"iptables", "-w", xtableLockWait, ruleAction, "fwd_input_" + tapDevice}, [][]string{
		// Reject out-going from tctap1 to private subnets
		{"-d", "10.0.0.0/8", "-j", "REJECT", "--reject-with", "icmp-net-unreachable"},
		{"-d", "172.16.0.0/12", "-j", "REJECT", "--reject-with", "icmp-net-unreachable"},
		{"-d", "169.254.0.0/16", "-j", "REJECT", "--reject-with", "icmp-net-unreachable"},
		{"-d", "192.168.0.0/16", "-j", "REJECT", "--reject-with", "icmp-net-unreachable"},
		// Allow out-going from tctap1 with correct source subnet
		{"-o", "eth0", "-s", subnet, "-j", "ACCEPT"},
		// Allow tctap1 -> tctap1 within allowed subnet
		{"-o", "tctap1", "-s", subnet, "-j", "ACCEPT"},
		// Reject all other input for forwarding from tap-device
		{"-j", "REJECT", "--reject-with", "icmp-net-prohibited"},
	})

	// Rules for filtering FORWARD to this tap device
	forwardOutputRules := prefixCommands([]string{"iptables", "-w", xtableLockWait, ruleAction, "fwd_output_" + tapDevice}, [][]string{
		// Reject incoming from private subnets to tctap1
		{"-s", "10.0.0.0/8", "-j", "DROP"},
		{"-s", "172.16.0.0/12", "-j", "DROP"},
		{"-s", "169.254.0.0/16", "-j", "DROP"},
		{"-s", "192.168.0.0/16", "-j", "DROP"},
		// Allow incoming from tctap1 with correct destination (if already established)
		{"-i", "eth0", "-d", subnet, "-m", "state", "--state", "RELATED,ESTABLISHED", "-j", "ACCEPT"},
		// Allow tctap1 -> tctap1 within allowed subnet
		{"-i", "tctap1", "-s", subnet, "-j", "ACCEPT"},
		// Reject all other output from forwarding to tap-device
		{"-j", "DROP"},
	})

	cmds := [][]string{}
	if !delete {
		cmds = append(cmds, nat...)
		cmds = append(cmds, chains...)
		cmds = append(cmds, rules...)
		cmds = append(cmds, inputRules...)
		cmds = append(cmds, outputRules...)
		cmds = append(cmds, forwardOutputRules...)
		cmds = append(cmds, forwardInputRules...)
	} else {
		// Reverse order when deleting, because we can't delete chains that are
		// referenced by a rule
		cmds = append(cmds, forwardInputRules...)
		cmds = append(cmds, forwardOutputRules...)
		cmds = append(cmds, outputRules...)
		cmds = append(cmds, inputRules...)
		cmds = append(cmds, rules...)
		cmds = append(cmds, chains...)
		cmds = append(cmds, nat...)
	}

	return cmds
}
<commit_msg>Fixed some hardcoded strings, fixing self-connections<commit_after>package network

// Maximum time to wait for the xtables lock when using iptables
const xtableLockWait = "3"

// ipTableRules returns a list of commands to append rules for tapDevice.
// If delete=false, this returns the commands to delete the rules.
func ipTableRules(tapDevice string, ipPrefix string, delete bool) [][]string {
	subnet := ipPrefix + ".0/24"
	gateway := ipPrefix + ".1"
	prefixCommands := func(prefix []string, rules [][]string) [][]string {
		cmds := [][]string{}
		for _, rule := range rules {
			cmds = append(cmds, append(prefix, rule...))
		}
		return cmds
	}

	ruleAction := "-A"
	chainAction := "-N"
	if delete {
		ruleAction = "-D"
		chainAction = "-X"
	}

	// Create/delete custom chains for this tap device
	chains := prefixCommands([]string{"iptables", "-w", xtableLockWait, chainAction}, [][]string{
		{"input_" + tapDevice},
		{"output_" + tapDevice},
		{"fwd_input_" + tapDevice},
		{"fwd_output_" + tapDevice},
	})

	// Rules for jumping to custom chains for this tap device
	rules := prefixCommands([]string{"iptables", "-w", xtableLockWait, ruleAction}, [][]string{
		{"INPUT", "-i", tapDevice, "-j", "input_" + tapDevice},
		{"OUTPUT", "-o", tapDevice, "-j", "output_" + tapDevice},
		{"FORWARD", "-i", tapDevice, "-j", "fwd_input_" + tapDevice},
		{"FORWARD", "-o", tapDevice, "-j", "fwd_output_" + tapDevice},
	})

	// Rules for nat from this subnet
	nat := prefixCommands([]string{"iptables", "-w", xtableLockWait, "-t", "nat", ruleAction}, [][]string{
		{"POSTROUTING", "-o", "eth0", "-s", subnet, "-j", "MASQUERADE"},
	})

	// Rules for filtering INPUT from this tap device
	inputRules := prefixCommands([]string{"iptables", "-w", xtableLockWait, ruleAction, "input_" + tapDevice}, [][]string{
		// Allow requests to meta-data service (from subnet only)
		{"-p", "tcp", "-s", subnet, "-d", metaDataIP, "-m", "tcp", "--dport", "80", "-m", "state", "--state", "NEW,ESTABLISHED", "-j", "ACCEPT"},
		// Allow DNS requests
		{"-p", "tcp", "-s", subnet, "-d", gateway, "-m", "tcp", "--dport", "53", "-m", "state", "--state", "NEW,ESTABLISHED", "-j", "ACCEPT"},
		{"-p", "udp", "-s", subnet, "-d", gateway, "-m", "udp", "--dport", "53", "-m", "state", "--state", "NEW,ESTABLISHED", "-j", "ACCEPT"},
		// Allow DCHP requests
		{"-s", "0.0.0.0", "-d", "255.255.255.255", "-p", "udp", "-m", "udp", "--sport", "68", "--dport", "67", "-j", "ACCEPT"},
		{"-s", subnet, "-d", gateway, "-p", "udp", "-m", "udp", "--sport", "68", "--dport", "67", "-j", "ACCEPT"},
		// Reject all other input (with special case for wrong port on meta-data service)
		{"-s", subnet, "-d", metaDataIP, "-j", "REJECT", "--reject-with", "icmp-port-unreachable"},
		{"-j", "REJECT", "--reject-with", "icmp-host-unreachable"},
	})

	// Rules for filtering OUTPUT to this tap device
	outputRules := prefixCommands([]string{"iptables", "-w", xtableLockWait, ruleAction, "output_" + tapDevice}, [][]string{
		// Allow meta-data replies (to subnet only)
		{"-p", "tcp", "-s", metaDataIP, "-d", subnet, "-m", "tcp", "--sport", "80", "-m", "state", "--state", "ESTABLISHED", "-j", "ACCEPT"},
		// Allow DNS replies from dnsmasq (to subnet only)
		{"-p", "udp", "-s", gateway, "-d", subnet, "-m", "udp", "--sport", "53", "-m", "state", "--state", "ESTABLISHED", "-j", "ACCEPT"},
		{"-p", "tcp", "-s", gateway, "-d", subnet, "-m", "tcp", "--sport", "53", "-m", "state", "--state", "ESTABLISHED", "-j", "ACCEPT"},
		// Allow DHCP replies
		{"-p", "udp", "-s", gateway, "-m", "udp", "--sport", "67", "--dport", "68", "-j", "ACCEPT"},
		// Reject all other output
		{"-j", "REJECT", "--reject-with", "icmp-net-prohibited"},
	})

	// Rules for filtering FORWARD from this tap device
	forwardInputRules := prefixCommands([]string{"iptables", "-w", xtableLockWait, ruleAction, "fwd_input_" + tapDevice}, [][]string{
		// Reject out-going from this tap device to private subnets
		{"-d", "10.0.0.0/8", "-j", "REJECT", "--reject-with", "icmp-net-unreachable"},
		{"-d", "172.16.0.0/12", "-j", "REJECT", "--reject-with", "icmp-net-unreachable"},
		{"-d", "169.254.0.0/16", "-j", "REJECT", "--reject-with", "icmp-net-unreachable"},
		{"-d", "192.168.0.0/16", "-j", "REJECT", "--reject-with", "icmp-net-unreachable"},
		// Allow out-going from this tap device with correct source subnet
		{"-o", "eth0", "-s", subnet, "-j", "ACCEPT"},
		// Allow tap device -> tap device within allowed subnet
		{"-o", tapDevice, "-s", subnet, "-j", "ACCEPT"},
		// Reject all other input for forwarding from tap-device
		{"-j", "REJECT", "--reject-with", "icmp-net-prohibited"},
	})

	// Rules for filtering FORWARD to this tap device
	forwardOutputRules := prefixCommands([]string{"iptables", "-w", xtableLockWait, ruleAction, "fwd_output_" + tapDevice}, [][]string{
		// Reject incoming from private subnets to this tap device
		{"-s", "10.0.0.0/8", "-j", "DROP"},
		{"-s", "172.16.0.0/12", "-j", "DROP"},
		{"-s", "169.254.0.0/16", "-j", "DROP"},
		{"-s", "192.168.0.0/16", "-j", "DROP"},
		// Allow incoming from this tap device with correct destination (if already established)
		{"-i", "eth0", "-d", subnet, "-m", "state", "--state", "RELATED,ESTABLISHED", "-j", "ACCEPT"},
		// Allow tap device -> tap device within allowed subnet
		{"-i", tapDevice, "-s", subnet, "-j", "ACCEPT"},
		// Reject all other output from forwarding to tap-device
		{"-j", "DROP"},
	})

	cmds := [][]string{}
	if !delete {
		cmds = append(cmds, nat...)
		cmds = append(cmds, chains...)
		cmds = append(cmds, rules...)
		cmds = append(cmds, inputRules...)
		cmds = append(cmds, outputRules...)
		cmds = append(cmds, forwardOutputRules...)
		cmds = append(cmds, forwardInputRules...)
	} else {
		// Reverse order when deleting, because we can't delete chains that are
		// referenced by a rule
		cmds = append(cmds, forwardInputRules...)
		cmds = append(cmds, forwardOutputRules...)
		cmds = append(cmds, outputRules...)
		cmds = append(cmds, inputRules...)
		cmds = append(cmds, rules...)
		cmds = append(cmds, chains...)
		cmds = append(cmds, nat...)
	}

	return cmds
}
<|endoftext|>


<commit_before>package relay_test

import (
	"context"
	"net"
	"sync"
	"testing"
	"time"

	libp2p "github.com/libp2p/go-libp2p"
	config "github.com/libp2p/go-libp2p/config"
	relay "github.com/libp2p/go-libp2p/p2p/host/relay"

	ggio "github.com/gogo/protobuf/io"
	cid "github.com/ipfs/go-cid"
	autonat "github.com/libp2p/go-libp2p-autonat"
	autonatpb "github.com/libp2p/go-libp2p-autonat/pb"
	circuit "github.com/libp2p/go-libp2p-circuit"
	host "github.com/libp2p/go-libp2p-host"
	inet "github.com/libp2p/go-libp2p-net"
	peer "github.com/libp2p/go-libp2p-peer"
	pstore "github.com/libp2p/go-libp2p-peerstore"
	routing "github.com/libp2p/go-libp2p-routing"
	ma "github.com/multiformats/go-multiaddr"
	manet "github.com/multiformats/go-multiaddr-net"
)

// test specific parameters
func init() {
	autonat.AutoNATIdentifyDelay = 10 * time.Millisecond
	autonat.AutoNATBootDelay = 1 * time.Second
	relay.BootDelay = 1 * time.Second
	manet.Private4 = []*net.IPNet{}
}

// mock routing
type mockRoutingTable struct {
	mx        sync.Mutex
	providers map[string]map[peer.ID]pstore.PeerInfo
}

type mockRouting struct {
	h   host.Host
	tab *mockRoutingTable
}

func newMockRoutingTable() *mockRoutingTable {
	return &mockRoutingTable{providers: make(map[string]map[peer.ID]pstore.PeerInfo)}
}

func newMockRouting(h host.Host, tab *mockRoutingTable) *mockRouting {
	return &mockRouting{h: h, tab: tab}
}

func (m *mockRouting) FindPeer(ctx context.Context, p peer.ID) (pstore.PeerInfo, error) {
	return pstore.PeerInfo{}, routing.ErrNotFound
}

func (m *mockRouting) Provide(ctx context.Context, cid cid.Cid, bcast bool) error {
	m.tab.mx.Lock()
	defer m.tab.mx.Unlock()

	pmap, ok := m.tab.providers[cid.String()]
	if !ok {
		pmap = make(map[peer.ID]pstore.PeerInfo)
		m.tab.providers[cid.String()] = pmap
	}

	pmap[m.h.ID()] = pstore.PeerInfo{ID: m.h.ID(), Addrs: m.h.Addrs()}

	return nil
}

func (m *mockRouting) FindProvidersAsync(ctx context.Context, cid cid.Cid, limit int) <-chan pstore.PeerInfo {
	ch := make(chan pstore.PeerInfo)
	go func() {
		defer close(ch)
		m.tab.mx.Lock()
		defer m.tab.mx.Unlock()

		pmap, ok := m.tab.providers[cid.String()]
		if !ok {
			return
		}

		for _, pi := range pmap {
			select {
			case ch <- pi:
			case <-ctx.Done():
				return
			}
		}
	}()

	return ch
}

// mock autonat
func makeAutoNATServicePrivate(ctx context.Context, t *testing.T) host.Host {
	h, err := libp2p.New(ctx)
	if err != nil {
		t.Fatal(err)
	}
	h.SetStreamHandler(autonat.AutoNATProto, sayAutoNATPrivate)
	return h
}

func sayAutoNATPrivate(s inet.Stream) {
	defer s.Close()
	w := ggio.NewDelimitedWriter(s)
	res := autonatpb.Message{
		Type:         autonatpb.Message_DIAL_RESPONSE.Enum(),
		DialResponse: newDialResponseError(autonatpb.Message_E_DIAL_ERROR, "no dialable addresses"),
	}
	w.WriteMsg(&res)
}

func newDialResponseError(status autonatpb.Message_ResponseStatus, text string) *autonatpb.Message_DialResponse {
	dr := new(autonatpb.Message_DialResponse)
	dr.Status = status.Enum()
	dr.StatusText = &text
	return dr
}

// connector
func connect(t *testing.T, a, b host.Host) {
	pinfo := pstore.PeerInfo{ID: a.ID(), Addrs: a.Addrs()}
	err := b.Connect(context.Background(), pinfo)
	if err != nil {
		t.Fatal(err)
	}
}

// and the actual test!
func TestAutoRelay(t *testing.T) {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	mtab := newMockRoutingTable()
	makeRouting := func(h host.Host) (config.Routing, error) {
		mr := newMockRouting(h, mtab)
		return mr, nil
	}

	h1 := makeAutoNATServicePrivate(ctx, t)
	_, err := libp2p.New(ctx, libp2p.EnableRelay(circuit.OptHop), libp2p.Routing(makeRouting))
	if err != nil {
		t.Fatal(err)
	}
	h3, err := libp2p.New(ctx, libp2p.EnableRelay(), libp2p.Routing(makeRouting))
	if err != nil {
		t.Fatal(err)
	}
	h4, err := libp2p.New(ctx, libp2p.EnableRelay())

	connect(t, h1, h3)
	time.Sleep(3 * time.Second)

	// verify that we advertise relay addrs
	haveRelay := false
	for _, addr := range h3.Addrs() {
		_, err := addr.ValueForProtocol(circuit.P_CIRCUIT)
		if err != nil {
			haveRelay = true
			break
		}
	}

	if !haveRelay {
		t.Fatal("No relay addrs advertised")
	}

	// check that we can connect through the relay
	var raddrs []ma.Multiaddr
	for _, addr := range h3.Addrs() {
		_, err := addr.ValueForProtocol(circuit.P_CIRCUIT)
		if err != nil {
			raddrs = append(raddrs, addr)
		}
	}

	err = h4.Connect(ctx, pstore.PeerInfo{h3.ID(), raddrs})
	if err != nil {
		t.Fatal(err)
	}
}
<commit_msg>fix inverted address selection logic in test<commit_after>package relay_test

import (
	"context"
	"net"
	"sync"
	"testing"
	"time"

	libp2p "github.com/libp2p/go-libp2p"
	config "github.com/libp2p/go-libp2p/config"
	relay "github.com/libp2p/go-libp2p/p2p/host/relay"

	ggio "github.com/gogo/protobuf/io"
	cid "github.com/ipfs/go-cid"
	autonat "github.com/libp2p/go-libp2p-autonat"
	autonatpb "github.com/libp2p/go-libp2p-autonat/pb"
	circuit "github.com/libp2p/go-libp2p-circuit"
	host "github.com/libp2p/go-libp2p-host"
	inet "github.com/libp2p/go-libp2p-net"
	peer "github.com/libp2p/go-libp2p-peer"
	pstore "github.com/libp2p/go-libp2p-peerstore"
	routing "github.com/libp2p/go-libp2p-routing"
	ma "github.com/multiformats/go-multiaddr"
	manet "github.com/multiformats/go-multiaddr-net"
)

// test specific parameters
func init() {
	autonat.AutoNATIdentifyDelay = 10 * time.Millisecond
	autonat.AutoNATBootDelay = 1 * time.Second
	relay.BootDelay = 1 * time.Second
	manet.Private4 = []*net.IPNet{}
}

// mock routing
type mockRoutingTable struct {
	mx        sync.Mutex
	providers map[string]map[peer.ID]pstore.PeerInfo
}

type mockRouting struct {
	h   host.Host
	tab *mockRoutingTable
}

func newMockRoutingTable() *mockRoutingTable {
	return &mockRoutingTable{providers: make(map[string]map[peer.ID]pstore.PeerInfo)}
}

func newMockRouting(h host.Host, tab *mockRoutingTable) *mockRouting {
	return &mockRouting{h: h, tab: tab}
}

func (m *mockRouting) FindPeer(ctx context.Context, p peer.ID) (pstore.PeerInfo, error) {
	return pstore.PeerInfo{}, routing.ErrNotFound
}

func (m *mockRouting) Provide(ctx context.Context, cid cid.Cid, bcast bool) error {
	m.tab.mx.Lock()
	defer m.tab.mx.Unlock()

	pmap, ok := m.tab.providers[cid.String()]
	if !ok {
		pmap = make(map[peer.ID]pstore.PeerInfo)
		m.tab.providers[cid.String()] = pmap
	}

	pmap[m.h.ID()] = pstore.PeerInfo{ID: m.h.ID(), Addrs: m.h.Addrs()}

	return nil
}

func (m *mockRouting) FindProvidersAsync(ctx context.Context, cid cid.Cid, limit int) <-chan pstore.PeerInfo {
	ch := make(chan pstore.PeerInfo)
	go func() {
		defer close(ch)
		m.tab.mx.Lock()
		defer m.tab.mx.Unlock()

		pmap, ok := m.tab.providers[cid.String()]
		if !ok {
			return
		}

		for _, pi := range pmap {
			select {
			case ch <- pi:
			case <-ctx.Done():
				return
			}
		}
	}()

	return ch
}

// mock autonat
func makeAutoNATServicePrivate(ctx context.Context, t *testing.T) host.Host {
	h, err := libp2p.New(ctx)
	if err != nil {
		t.Fatal(err)
	}
	h.SetStreamHandler(autonat.AutoNATProto, sayAutoNATPrivate)
	return h
}

func sayAutoNATPrivate(s inet.Stream) {
	defer s.Close()
	w := ggio.NewDelimitedWriter(s)
	res := autonatpb.Message{
		Type:         autonatpb.Message_DIAL_RESPONSE.Enum(),
		DialResponse: newDialResponseError(autonatpb.Message_E_DIAL_ERROR, "no dialable addresses"),
	}
	w.WriteMsg(&res)
}

func newDialResponseError(status autonatpb.Message_ResponseStatus, text string) *autonatpb.Message_DialResponse {
	dr := new(autonatpb.Message_DialResponse)
	dr.Status = status.Enum()
	dr.StatusText = &text
	return dr
}

// connector
func connect(t *testing.T, a, b host.Host) {
	pinfo := pstore.PeerInfo{ID: a.ID(), Addrs: a.Addrs()}
	err := b.Connect(context.Background(), pinfo)
	if err != nil {
		t.Fatal(err)
	}
}

// and the actual test!
func TestAutoRelay(t *testing.T) {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	mtab := newMockRoutingTable()
	makeRouting := func(h host.Host) (config.Routing, error) {
		mr := newMockRouting(h, mtab)
		return mr, nil
	}

	h1 := makeAutoNATServicePrivate(ctx, t)
	_, err := libp2p.New(ctx, libp2p.EnableRelay(circuit.OptHop), libp2p.Routing(makeRouting))
	if err != nil {
		t.Fatal(err)
	}
	h3, err := libp2p.New(ctx, libp2p.EnableRelay(), libp2p.Routing(makeRouting))
	if err != nil {
		t.Fatal(err)
	}
	h4, err := libp2p.New(ctx, libp2p.EnableRelay())

	connect(t, h1, h3)
	time.Sleep(3 * time.Second)

	// verify that we advertise relay addrs
	haveRelay := false
	for _, addr := range h3.Addrs() {
		_, err := addr.ValueForProtocol(circuit.P_CIRCUIT)
		if err == nil {
			haveRelay = true
			break
		}
	}

	if !haveRelay {
		t.Fatal("No relay addrs advertised")
	}

	// check that we can connect through the relay
	var raddrs []ma.Multiaddr
	for _, addr := range h3.Addrs() {
		_, err := addr.ValueForProtocol(circuit.P_CIRCUIT)
		if err == nil {
			raddrs = append(raddrs, addr)
		}
	}

	err = h4.Connect(ctx, pstore.PeerInfo{h3.ID(), raddrs})
	if err != nil {
		t.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package common

import (
	gossh "code.google.com/p/go.crypto/ssh"
	"errors"
	"fmt"
	"github.com/mitchellh/multistep"
	"github.com/mitchellh/packer/communicator/ssh"
	"github.com/mitchellh/packer/packer"
	"log"
	"strings"
	"time"
)

// StepConnectSSH is a multistep Step implementation that waits for SSH
// to become available. It gets the connection information from a single
// configuration when creating the step.
//
// Uses:
//   ui packer.Ui
//
// Produces:
//   communicator packer.Communicator
type StepConnectSSH struct {
	// SSHAddress is a function that returns the TCP address to connect to
	// for SSH. This is a function so that you can query information
	// if necessary for this address.
	SSHAddress func(multistep.StateBag) (string, error)

	// SSHConfig is a function that returns the proper client configuration
	// for SSH access.
	SSHConfig func(multistep.StateBag) (*gossh.ClientConfig, error)

	// SSHWaitTimeout is the total timeout to wait for SSH to become available.
	SSHWaitTimeout time.Duration

	// NoPty, if true, will not request a Pty from the remote end.
	NoPty bool

	comm packer.Communicator
}

func (s *StepConnectSSH) Run(state multistep.StateBag) multistep.StepAction {
	ui := state.Get("ui").(packer.Ui)

	var comm packer.Communicator
	var err error

	cancel := make(chan struct{})
	waitDone := make(chan bool, 1)
	go func() {
		ui.Say("Waiting for SSH to become available...")
		comm, err = s.waitForSSH(state, cancel)
		waitDone <- true
	}()

	log.Printf("Waiting for SSH, up to timeout: %s", s.SSHWaitTimeout)
	timeout := time.After(s.SSHWaitTimeout)
WaitLoop:
	for {
		// Wait for either SSH to become available, a timeout to occur,
		// or an interrupt to come through.
		select {
		case <-waitDone:
			if err != nil {
				ui.Error(fmt.Sprintf("Error waiting for SSH: %s", err))
				return multistep.ActionHalt
			}

			ui.Say("Connected to SSH!")
			s.comm = comm
			state.Put("communicator", comm)
			break WaitLoop
		case <-timeout:
			ui.Error("Timeout waiting for SSH.")
			close(cancel)
			return multistep.ActionHalt
		case <-time.After(1 * time.Second):
			if _, ok := state.GetOk(multistep.StateCancelled); ok {
				// The step sequence was cancelled, so cancel waiting for SSH
				// and just start the halting process.
				close(cancel)
				log.Println("Interrupt detected, quitting waiting for SSH.")
				return multistep.ActionHalt
			}
		}
	}

	return multistep.ActionContinue
}

func (s *StepConnectSSH) Cleanup(multistep.StateBag) {
}

func (s *StepConnectSSH) waitForSSH(state multistep.StateBag, cancel <-chan struct{}) (packer.Communicator, error) {
	handshakeAttempts := 0

	var comm packer.Communicator
	for {
		select {
		case <-cancel:
			log.Println("SSH wait cancelled. Exiting loop.")
			return nil, errors.New("SSH wait cancelled")
		case <-time.After(5 * time.Second):
		}

		// First we request the TCP connection information
		address, err := s.SSHAddress(state)
		if err != nil {
			log.Printf("Error getting SSH address: %s", err)
			continue
		}

		// Retrieve the SSH configuration
		sshConfig, err := s.SSHConfig(state)
		if err != nil {
			log.Printf("Error getting SSH config: %s", err)
			continue
		}

		// Attempt to connect to SSH port
		connFunc := ssh.ConnectFunc("tcp", address)
		nc, err := connFunc()
		if err != nil {
			log.Printf("TCP connection to SSH ip/port failed: %s", err)
			continue
		}
		nc.Close()

		// Then we attempt to connect via SSH
		config := &ssh.Config{
			Connection: connFunc,
			SSHConfig:  sshConfig,
			NoPty:      s.NoPty,
		}

		log.Println("Attempting SSH connection...")
		comm, err = ssh.New(config)
		if err != nil {
			log.Printf("SSH handshake err: %s", err)

			// Only count this as an attempt if we were able to attempt
			// to authenticate. Note this is very brittle since it depends
			// on the string of the error... but I don't see any other way.
			if strings.Contains(err.Error(), "authenticate") {
				log.Printf("Detected authentication error. Increasing handshake attempts.")
				handshakeAttempts += 1
			}

			if handshakeAttempts < 10 {
				// Try to connect via SSH a handful of times
				continue
			}

			return nil, err
		}

		break
	}

	return comm, nil
}
<commit_msg>An SSH timeout should be treated as an error<commit_after>package common

import (
	gossh "code.google.com/p/go.crypto/ssh"
	"errors"
	"fmt"
	"github.com/mitchellh/multistep"
	"github.com/mitchellh/packer/communicator/ssh"
	"github.com/mitchellh/packer/packer"
	"log"
	"strings"
	"time"
)

// StepConnectSSH is a multistep Step implementation that waits for SSH
// to become available. It gets the connection information from a single
// configuration when creating the step.
//
// Uses:
//   ui packer.Ui
//
// Produces:
//   communicator packer.Communicator
type StepConnectSSH struct {
	// SSHAddress is a function that returns the TCP address to connect to
	// for SSH. This is a function so that you can query information
	// if necessary for this address.
	SSHAddress func(multistep.StateBag) (string, error)

	// SSHConfig is a function that returns the proper client configuration
	// for SSH access.
	SSHConfig func(multistep.StateBag) (*gossh.ClientConfig, error)

	// SSHWaitTimeout is the total timeout to wait for SSH to become available.
	SSHWaitTimeout time.Duration

	// NoPty, if true, will not request a Pty from the remote end.
	NoPty bool

	comm packer.Communicator
}

func (s *StepConnectSSH) Run(state multistep.StateBag) multistep.StepAction {
	ui := state.Get("ui").(packer.Ui)

	var comm packer.Communicator
	var err error

	cancel := make(chan struct{})
	waitDone := make(chan bool, 1)
	go func() {
		ui.Say("Waiting for SSH to become available...")
		comm, err = s.waitForSSH(state, cancel)
		waitDone <- true
	}()

	log.Printf("Waiting for SSH, up to timeout: %s", s.SSHWaitTimeout)
	timeout := time.After(s.SSHWaitTimeout)
WaitLoop:
	for {
		// Wait for either SSH to become available, a timeout to occur,
		// or an interrupt to come through.
		select {
		case <-waitDone:
			if err != nil {
				ui.Error(fmt.Sprintf("Error waiting for SSH: %s", err))
				return multistep.ActionHalt
			}

			ui.Say("Connected to SSH!")
			s.comm = comm
			state.Put("communicator", comm)
			break WaitLoop
		case <-timeout:
			err := fmt.Errorf("Timeout waiting for SSH.")
			state.Put("error", err)
			ui.Error(err.Error())
			close(cancel)
			return multistep.ActionHalt
		case <-time.After(1 * time.Second):
			if _, ok := state.GetOk(multistep.StateCancelled); ok {
				// The step sequence was cancelled, so cancel waiting for SSH
				// and just start the halting process.
				close(cancel)
				log.Println("Interrupt detected, quitting waiting for SSH.")
				return multistep.ActionHalt
			}
		}
	}

	return multistep.ActionContinue
}

func (s *StepConnectSSH) Cleanup(multistep.StateBag) {
}

func (s *StepConnectSSH) waitForSSH(state multistep.StateBag, cancel <-chan struct{}) (packer.Communicator, error) {
	handshakeAttempts := 0

	var comm packer.Communicator
	for {
		select {
		case <-cancel:
			log.Println("SSH wait cancelled. Exiting loop.")
			return nil, errors.New("SSH wait cancelled")
		case <-time.After(5 * time.Second):
		}

		// First we request the TCP connection information
		address, err := s.SSHAddress(state)
		if err != nil {
			log.Printf("Error getting SSH address: %s", err)
			continue
		}

		// Retrieve the SSH configuration
		sshConfig, err := s.SSHConfig(state)
		if err != nil {
			log.Printf("Error getting SSH config: %s", err)
			continue
		}

		// Attempt to connect to SSH port
		connFunc := ssh.ConnectFunc("tcp", address)
		nc, err := connFunc()
		if err != nil {
			log.Printf("TCP connection to SSH ip/port failed: %s", err)
			continue
		}
		nc.Close()

		// Then we attempt to connect via SSH
		config := &ssh.Config{
			Connection: connFunc,
			SSHConfig:  sshConfig,
			NoPty:      s.NoPty,
		}

		log.Println("Attempting SSH connection...")
		comm, err = ssh.New(config)
		if err != nil {
			log.Printf("SSH handshake err: %s", err)

			// Only count this as an attempt if we were able to attempt
			// to authenticate. Note this is very brittle since it depends
			// on the string of the error... but I don't see any other way.
			if strings.Contains(err.Error(), "authenticate") {
				log.Printf("Detected authentication error. Increasing handshake attempts.")
				handshakeAttempts += 1
			}

			if handshakeAttempts < 10 {
				// Try to connect via SSH a handful of times
				continue
			}

			return nil, err
		}

		break
	}

	return comm, nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"github.com/howeyc/fsnotify"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"
)

func main() {
	watcher, _ = fsnotify.NewWatcher() // TODO: err
	defer watcher.Close()

	fmt.Println("Initialized watcher...")

	go reactToChanges()

	working, _ := os.Getwd() // TODO: err
	updateWatch(working)

	http.HandleFunc("/", homeHandler)
	http.HandleFunc("/latest", reportHandler)
	http.HandleFunc("/watch", watchHandler)
	http.ListenAndServe(":8080", nil) // TODO: flag for port
}

func updateWatch(root string) {
	addNewWatches(root)
	removeOldWatches()
}
func addNewWatches(root string) {
	if rootWatch != root {
		adjustRoot(root)
	}

	watchNestedPaths(root)
}
func adjustRoot(root string) {
	fmt.Println("Watching new root:", root)
	for path, _ := range watched {
		removeWatch(path)
	}
	rootWatch = root
	watch(root)
}
func watchNestedPaths(root string) {
	filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
		if matches, _ := filepath.Glob(filepath.Join(path, "*test.go")); len(matches) > 0 {
			watch(path)
		}
		return nil
	})
}
func watch(path string) {
	if !watching(path) {
		fmt.Println("Watching:", path)
		watched[path] = true
		watcher.Watch(path)
	}
}

func watching(path string) bool {
	for w, _ := range watched {
		if w == path {
			return true
		}
	}
	return false
}

func removeOldWatches() {
	for path, _ := range watched {
		if !exists(path) {
			removeWatch(path)
		}
	}
}
func removeWatch(path string) {
	delete(watched, path)
	watcher.RemoveWatch(path)
	fmt.Println("No longer watching:", path)
}

func exists(path string) bool {
	info, err := os.Stat(path)
	return err == nil && info.IsDir()
}

func reactToChanges() {
	busy := false
	done := make(chan bool)
	ready := make(chan bool)

	for {
		select {
		case ev := <-watcher.Event:
			updateWatch(rootWatch)
			if strings.HasSuffix(ev.Name, ".go") && !busy {
				busy = true
				go runTests(done)
			}

		case err := <-watcher.Error:
			fmt.Println(err)

		case <-done:
			// TODO: rethink this delay?
			time.AfterFunc(1500*time.Millisecond, func() {
				ready <- true
			})

		case <-ready:
			busy = false
		}
	}
}

func runTests(done chan bool) {
	results := []PackageResult{}
	for path, _ := range watched {
		fmt.Println("Running tests at:", path)
		os.Chdir(path)                                            // TODO: err
		output, _ := exec.Command("go", "test", "-json").Output() // TODO: err
		result := parsePackageResult(string(output))
		fmt.Println("Result: ", result.Passed)
		results = append(results, result)
	}
	serialized, _ := json.Marshal(results) // TODO: err
	latestOutput = string(serialized)
	done <- true
}

func homeHandler(writer http.ResponseWriter, request *http.Request) {
	fmt.Fprint(writer, "...") // TODO: setup static handler for html and javascript?
}

func reportHandler(writer http.ResponseWriter, request *http.Request) {
	writer.Header().Set("Content-Type", "application/json")
	fmt.Fprint(writer, latestOutput)
}

func watchHandler(writer http.ResponseWriter, request *http.Request) {
	if request.Method == "GET" {
		writer.Write([]byte(rootWatch))
		return
	}

	value := request.URL.Query()["root"]
	if len(value) == 0 {
		http.Error(writer, "No 'root' query string parameter included!", http.StatusBadRequest)
		return
	}
	newRoot := value[0]
	if !exists(newRoot) {
		http.Error(writer, "The 'root' value provided is not an existing directory.", http.StatusNotFound)
	} else {
		updateWatch(newRoot)
	}
}

var (
	latestOutput string
	rootWatch    string
	watched      map[string]bool
	watcher      *fsnotify.Watcher
)
<commit_msg>Renames.<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"github.com/howeyc/fsnotify"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"
)

func main() {
	watcher, _ = fsnotify.NewWatcher() // TODO: err
	defer watcher.Close()

	fmt.Println("Initialized watcher...")

	go reactToChanges()

	working, _ := os.Getwd() // TODO: err
	updateWatch(working)

	http.HandleFunc("/", homeHandler)
	http.HandleFunc("/latest", reportHandler)
	http.HandleFunc("/watch", watchHandler)
	http.ListenAndServe(":8080", nil) // TODO: flag for port
}

func updateWatch(root string) {
	addWatches(root)
	removeWatches()
}
func addWatches(root string) {
	if rootWatch != root {
		adjustRoot(root)
	}

	watchNestedPaths(root)
}
func adjustRoot(root string) {
	fmt.Println("Watching new root:", root)
	for path, _ := range watched {
		removeWatch(path)
	}
	rootWatch = root
	watch(root)
}
func watchNestedPaths(root string) {
	filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
		if matches, _ := filepath.Glob(filepath.Join(path, "*test.go")); len(matches) > 0 {
			watch(path)
		}
		return nil
	})
}
func watch(path string) {
	if !watching(path) {
		fmt.Println("Watching:", path)
		watched[path] = true
		watcher.Watch(path)
	}
}

func watching(path string) bool {
	for w, _ := range watched {
		if w == path {
			return true
		}
	}
	return false
}

func removeWatches() {
	for path, _ := range watched {
		if !exists(path) {
			removeWatch(path)
		}
	}
}
func removeWatch(path string) {
	delete(watched, path)
	watcher.RemoveWatch(path)
	fmt.Println("No longer watching:", path)
}

func exists(path string) bool {
	info, err := os.Stat(path)
	return err == nil && info.IsDir()
}

func reactToChanges() {
	busy := false
	done := make(chan bool)
	ready := make(chan bool)

	for {
		select {
		case ev := <-watcher.Event:
			updateWatch(rootWatch)
			if strings.HasSuffix(ev.Name, ".go") && !busy {
				busy = true
				go runTests(done)
			}

		case err := <-watcher.Error:
			fmt.Println(err)

		case <-done:
			// TODO: rethink this delay?
			time.AfterFunc(1500*time.Millisecond, func() {
				ready <- true
			})

		case <-ready:
			busy = false
		}
	}
}

func runTests(done chan bool) {
	results := []PackageResult{}
	for path, _ := range watched {
		fmt.Println("Running tests at:", path)
		os.Chdir(path)                                            // TODO: err
		output, _ := exec.Command("go", "test", "-json").Output() // TODO: err
		result := parsePackageResult(string(output))
		fmt.Println("Result: ", result.Passed)
		results = append(results, result)
	}
	serialized, _ := json.Marshal(results) // TODO: err
	latestOutput = string(serialized)
	done <- true
}

func homeHandler(writer http.ResponseWriter, request *http.Request) {
	fmt.Fprint(writer, "...") // TODO: setup static handler for html and javascript?
}

func reportHandler(writer http.ResponseWriter, request *http.Request) {
	writer.Header().Set("Content-Type", "application/json")
	fmt.Fprint(writer, latestOutput)
}

func watchHandler(writer http.ResponseWriter, request *http.Request) {
	if request.Method == "GET" {
		writer.Write([]byte(rootWatch))
		return
	}

	value := request.URL.Query()["root"]
	if len(value) == 0 {
		http.Error(writer, "No 'root' query string parameter included!", http.StatusBadRequest)
		return
	}
	newRoot := value[0]
	if !exists(newRoot) {
		http.Error(writer, "The 'root' value provided is not an existing directory.", http.StatusNotFound)
	} else {
		updateWatch(newRoot)
	}
}

var (
	latestOutput string
	rootWatch    string
	watched      map[string]bool
	watcher      *fsnotify.Watcher
)
<|endoftext|>


<commit_before>package konnectorsauth

import (
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
	"time"

	"github.com/cozy/cozy-stack/pkg/accounts"
	"github.com/cozy/cozy-stack/pkg/consts"
	"github.com/cozy/cozy-stack/pkg/couchdb"
	"github.com/cozy/cozy-stack/pkg/instance"
	"github.com/cozy/cozy-stack/pkg/permissions"
	"github.com/cozy/cozy-stack/web/jsonapi"
	"github.com/cozy/cozy-stack/web/middlewares"
	webperm "github.com/cozy/cozy-stack/web/permissions"
	"github.com/cozy/echo"
)

type apiAccount struct {
	*accounts.Account
}

func (a *apiAccount) MarshalJSON() ([]byte, error)           { return json.Marshal(a.Account) }
func (a *apiAccount) Relationships() jsonapi.RelationshipMap { return nil }
func (a *apiAccount) Included() []jsonapi.Object             { return nil }
func (a *apiAccount) Links() *jsonapi.LinksList {
	return &jsonapi.LinksList{Self: "/data/" + consts.Accounts + "/" + a.ID()}
}

func start(c echo.Context) error {
	instance := middlewares.GetInstance(c)

	scope := c.QueryParam("scope")
	clientState := c.QueryParam("state")
	nonce := c.QueryParam("nonce")
	accountTypeID := c.Param("accountType")
	accountType, err := accounts.TypeInfo(accountTypeID)
	if err != nil {
		return err
	}

	state, err := getStorage().Add(&stateHolder{
		InstanceDomain: instance.Domain,
		AccountType:    accountType.ID(),
		ClientState:    clientState,
		Nonce:          nonce,
	})
	if err != nil {
		return err
	}

	url, err := accountType.MakeOauthStartURL(instance, scope, state)
	if err != nil {
		return err
	}

	return c.Redirect(http.StatusSeeOther, url)
}

func redirectToDataCollect(c echo.Context, account *accounts.Account, clientState string) error {
	instance := middlewares.GetInstance(c)
	u := instance.SubDomain(consts.CollectSlug)
	vv := &url.Values{}
	vv.Add("account", account.ID())
	if clientState != "" {
		vv.Add("state", clientState)
	}
	u.RawQuery = vv.Encode()
	return c.Redirect(http.StatusSeeOther, u.String())
}

// redirect is the redirect_uri endpoint passed to oauth services
// it should create the account.
// middlewares.NeedInstance is not applied before this handler
// it needs to handle both
// - with instance redirect
// - without instance redirect
func redirect(c echo.Context) error {

	accessCode := c.QueryParam("code")
	accessToken := c.QueryParam("access_token")
	accountTypeID := c.Param("accountType")
	accountType, err := accounts.TypeInfo(accountTypeID)
	if err != nil {
		return err
	}

	i, _ := instance.Get(c.Request().Host)

	if i != nil && accessToken != "" {
		if i == nil {
			return echo.NewHTTPError(http.StatusBadRequest,
				"using ?access_token with instance-less redirect")
		}
		account := &accounts.Account{
			AccountType: accountTypeID,
			Oauth: &accounts.OauthInfo{
				AccessToken: accessToken,
			},
		}
		err = couchdb.CreateDoc(i, account)
		if err != nil {
			return err
		}
		return redirectToDataCollect(c, account, "")
	}

	stateCode := c.QueryParam("state")
	state := getStorage().Find(stateCode)

	if state == nil ||
		state.AccountType != accountTypeID ||
		(i != nil && state.InstanceDomain != i.Domain) {
		return errors.New("bad state")
	}

	// TODO should we check if the req.Host is a given "_oauth_callback" domain?
	if i == nil {
		i, err = instance.Get(state.InstanceDomain)
		if err != nil {
			return errors.New("bad state")
		}
	}

	var req *http.Request

	data := url.Values{
		"grant_type":   []string{accounts.AuthorizationCode},
		"code":         []string{accessCode},
		"redirect_uri": []string{accountType.RedirectURI(i)},
		"state":        []string{stateCode},
		"nonce":        []string{state.Nonce},
	}

	if accountType.TokenAuthMode != accounts.BasicTokenAuthMode {
		data.Add("client_id", accountType.ClientID)
		data.Add("client_secret", accountType.ClientSecret)
	}

	body := data.Encode()
	req, err = http.NewRequest("POST", accountType.TokenEndpoint, strings.NewReader(body))
	if err != nil {
		return err
	}

	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	req.Header.Add("Accept", "application/json")

	if accountType.TokenAuthMode == accounts.BasicTokenAuthMode {
		auth := []byte(accountType.ClientID + ":" + accountType.ClientSecret)
		req.Header.Add("Authorization", "Basic "+base64.StdEncoding.EncodeToString(auth))
	}

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return err
	}

	resBody, err := ioutil.ReadAll(res.Body)

	if res.StatusCode != 200 {
		return errors.New("oauth services responded with non-200 res : " + string(resBody))
	}

	if err != nil {
		return err
	}

	var out struct {
		RefreshToken     string `json:"refresh_token"`
		AccessToken      string `json:"access_token"`
		IDToken          string `json:"id_token"` // alternative name for access_token
		ExpiresIn        int    `json:"expires_in"`
		TokenType        string `json:"token_type"`
		Error            string `json:"error"`
		ErrorDescription string `json:"error_description"`
	}
	err = json.Unmarshal(resBody, &out)
	if err != nil {
		return err
	}

	if out.Error != "" {
		return fmt.Errorf("OauthError(%s) %s", out.Error, out.ErrorDescription)
	}

	var ExpiresAt time.Time
	if out.ExpiresIn != 0 {
		ExpiresAt = time.Now().Add(time.Duration(out.ExpiresIn) * time.Second)
	}

	account := &accounts.Account{
		AccountType: accountType.ID(),
		Oauth:       &accounts.OauthInfo{ExpiresAt: ExpiresAt},
	}

	if out.AccessToken == "" {
		out.AccessToken = out.IDToken
	}

	if out.AccessToken == "" {
		return errors.New("server responded without access token")
	}

	account.Oauth.AccessToken = out.AccessToken
	account.Oauth.RefreshToken = out.RefreshToken
	account.Oauth.TokenType = out.TokenType

	// decode same resBody into a map for non-standard fields
	var extras map[string]interface{}
	json.Unmarshal(resBody, &extras)
	delete(extras, "access_token")
	delete(extras, "refresh_token")
	delete(extras, "token_type")
	delete(extras, "expires_in")

	if len(extras) > 0 {
		account.Extras = extras
	}

	err = couchdb.CreateDoc(i, account)
	if err != nil {
		return err
	}

	return redirectToDataCollect(c, account, state.ClientState)
}

// refresh is an internal route used by konnectors to refresh accounts
// it requires permissions GET:io.cozy.accounts:accountid
func refresh(c echo.Context) error {
	instance := middlewares.GetInstance(c)
	accountid := c.Param("accountid")

	var account accounts.Account
	if err := couchdb.GetDoc(instance, consts.Accounts, accountid, &account); err != nil {
		return err
	}

	if err := webperm.Allow(c, permissions.GET, &account); err != nil {
		return err
	}

	accountType, err := accounts.TypeInfo(account.AccountType)
	if err != nil {
		return err
	}

	err = accountType.RefreshAccount(account)
	if err != nil {
		return err
	}

	err = couchdb.UpdateDoc(instance, &account)
	if err != nil {
		return err
	}

	return jsonapi.Data(c, http.StatusOK, &apiAccount{&account}, nil)

}

// Routes setups routing for cozy-as-oauth-client routes
// Careful, the normal middlewares NeedInstance and LoadSession are not applied
// to this group in web/routing
func Routes(router *echo.Group) {
	router.GET("/:accountType/start", start, middlewares.NeedInstance)
	router.GET("/:accountType/redirect", redirect)
	router.POST("/:accountType/:accountid/refresh", refresh)
}
<commit_msg>fix: an error was never possible<commit_after>package konnectorsauth

import (
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
	"time"

	"github.com/cozy/cozy-stack/pkg/accounts"
	"github.com/cozy/cozy-stack/pkg/consts"
	"github.com/cozy/cozy-stack/pkg/couchdb"
	"github.com/cozy/cozy-stack/pkg/instance"
	"github.com/cozy/cozy-stack/pkg/permissions"
	"github.com/cozy/cozy-stack/web/jsonapi"
	"github.com/cozy/cozy-stack/web/middlewares"
	webperm "github.com/cozy/cozy-stack/web/permissions"
	"github.com/cozy/echo"
)

type apiAccount struct {
	*accounts.Account
}

func (a *apiAccount) MarshalJSON() ([]byte, error)           { return json.Marshal(a.Account) }
func (a *apiAccount) Relationships() jsonapi.RelationshipMap { return nil }
func (a *apiAccount) Included() []jsonapi.Object             { return nil }
func (a *apiAccount) Links() *jsonapi.LinksList {
	return &jsonapi.LinksList{Self: "/data/" + consts.Accounts + "/" + a.ID()}
}

func start(c echo.Context) error {
	instance := middlewares.GetInstance(c)

	scope := c.QueryParam("scope")
	clientState := c.QueryParam("state")
	nonce := c.QueryParam("nonce")
	accountTypeID := c.Param("accountType")
	accountType, err := accounts.TypeInfo(accountTypeID)
	if err != nil {
		return err
	}

	state, err := getStorage().Add(&stateHolder{
		InstanceDomain: instance.Domain,
		AccountType:    accountType.ID(),
		ClientState:    clientState,
		Nonce:          nonce,
	})
	if err != nil {
		return err
	}

	url, err := accountType.MakeOauthStartURL(instance, scope, state)
	if err != nil {
		return err
	}

	return c.Redirect(http.StatusSeeOther, url)
}

func redirectToDataCollect(c echo.Context, account *accounts.Account, clientState string) error {
	instance := middlewares.GetInstance(c)
	u := instance.SubDomain(consts.CollectSlug)
	vv := &url.Values{}
	vv.Add("account", account.ID())
	if clientState != "" {
		vv.Add("state", clientState)
	}
	u.RawQuery = vv.Encode()
	return c.Redirect(http.StatusSeeOther, u.String())
}

// redirect is the redirect_uri endpoint passed to oauth services
// it should create the account.
// middlewares.NeedInstance is not applied before this handler
// it needs to handle both
// - with instance redirect
// - without instance redirect
func redirect(c echo.Context) error {

	accessCode := c.QueryParam("code")
	accessToken := c.QueryParam("access_token")
	accountTypeID := c.Param("accountType")
	accountType, err := accounts.TypeInfo(accountTypeID)
	if err != nil {
		return err
	}

	i, _ := instance.Get(c.Request().Host)

	if i != nil && accessToken != "" {
		account := &accounts.Account{
			AccountType: accountTypeID,
			Oauth: &accounts.OauthInfo{
				AccessToken: accessToken,
			},
		}
		err = couchdb.CreateDoc(i, account)
		if err != nil {
			return err
		}
		return redirectToDataCollect(c, account, "")
	}

	if accessToken != "" {
		return echo.NewHTTPError(http.StatusBadRequest,
			"using ?access_token with instance-less redirect")
	}

	stateCode := c.QueryParam("state")
	state := getStorage().Find(stateCode)

	if state == nil ||
		state.AccountType != accountTypeID ||
		(i != nil && state.InstanceDomain != i.Domain) {
		return errors.New("bad state")
	}

	// TODO should we check if the req.Host is a given "_oauth_callback" domain?
	if i == nil {
		i, err = instance.Get(state.InstanceDomain)
		if err != nil {
			return errors.New("bad state")
		}
	}

	var req *http.Request

	data := url.Values{
		"grant_type":   []string{accounts.AuthorizationCode},
		"code":         []string{accessCode},
		"redirect_uri": []string{accountType.RedirectURI(i)},
		"state":        []string{stateCode},
		"nonce":        []string{state.Nonce},
	}

	if accountType.TokenAuthMode != accounts.BasicTokenAuthMode {
		data.Add("client_id", accountType.ClientID)
		data.Add("client_secret", accountType.ClientSecret)
	}

	body := data.Encode()
	req, err = http.NewRequest("POST", accountType.TokenEndpoint, strings.NewReader(body))
	if err != nil {
		return err
	}

	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	req.Header.Add("Accept", "application/json")

	if accountType.TokenAuthMode == accounts.BasicTokenAuthMode {
		auth := []byte(accountType.ClientID + ":" + accountType.ClientSecret)
		req.Header.Add("Authorization", "Basic "+base64.StdEncoding.EncodeToString(auth))
	}

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		return err
	}

	resBody, err := ioutil.ReadAll(res.Body)

	if res.StatusCode != 200 {
		return errors.New("oauth services responded with non-200 res : " + string(resBody))
	}

	if err != nil {
		return err
	}

	var out struct {
		RefreshToken     string `json:"refresh_token"`
		AccessToken      string `json:"access_token"`
		IDToken          string `json:"id_token"` // alternative name for access_token
		ExpiresIn        int    `json:"expires_in"`
		TokenType        string `json:"token_type"`
		Error            string `json:"error"`
		ErrorDescription string `json:"error_description"`
	}
	err = json.Unmarshal(resBody, &out)
	if err != nil {
		return err
	}

	if out.Error != "" {
		return fmt.Errorf("OauthError(%s) %s", out.Error, out.ErrorDescription)
	}

	var ExpiresAt time.Time
	if out.ExpiresIn != 0 {
		ExpiresAt = time.Now().Add(time.Duration(out.ExpiresIn) * time.Second)
	}

	account := &accounts.Account{
		AccountType: accountType.ID(),
		Oauth:       &accounts.OauthInfo{ExpiresAt: ExpiresAt},
	}

	if out.AccessToken == "" {
		out.AccessToken = out.IDToken
	}

	if out.AccessToken == "" {
		return errors.New("server responded without access token")
	}

	account.Oauth.AccessToken = out.AccessToken
	account.Oauth.RefreshToken = out.RefreshToken
	account.Oauth.TokenType = out.TokenType

	// decode same resBody into a map for non-standard fields
	var extras map[string]interface{}
	json.Unmarshal(resBody, &extras)
	delete(extras, "access_token")
	delete(extras, "refresh_token")
	delete(extras, "token_type")
	delete(extras, "expires_in")

	if len(extras) > 0 {
		account.Extras = extras
	}

	err = couchdb.CreateDoc(i, account)
	if err != nil {
		return err
	}

	return redirectToDataCollect(c, account, state.ClientState)
}

// refresh is an internal route used by konnectors to refresh accounts
// it requires permissions GET:io.cozy.accounts:accountid
func refresh(c echo.Context) error {
	instance := middlewares.GetInstance(c)
	accountid := c.Param("accountid")

	var account accounts.Account
	if err := couchdb.GetDoc(instance, consts.Accounts, accountid, &account); err != nil {
		return err
	}

	if err := webperm.Allow(c, permissions.GET, &account); err != nil {
		return err
	}

	accountType, err := accounts.TypeInfo(account.AccountType)
	if err != nil {
		return err
	}

	err = accountType.RefreshAccount(account)
	if err != nil {
		return err
	}

	err = couchdb.UpdateDoc(instance, &account)
	if err != nil {
		return err
	}

	return jsonapi.Data(c, http.StatusOK, &apiAccount{&account}, nil)

}

// Routes setups routing for cozy-as-oauth-client routes
// Careful, the normal middlewares NeedInstance and LoadSession are not applied
// to this group in web/routing
func Routes(router *echo.Group) {
	router.GET("/:accountType/start", start, middlewares.NeedInstance)
	router.GET("/:accountType/redirect", redirect)
	router.POST("/:accountType/:accountid/refresh", refresh)
}
<|endoftext|>