<commit_before>/*
Copyright 2017 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 customresource

import (
	"fmt"

	"github.com/go-openapi/validate"

	"k8s.io/apimachinery/pkg/api/meta"
	"k8s.io/apimachinery/pkg/api/validation"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/fields"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/util/validation/field"
	genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
	"k8s.io/apiserver/pkg/storage"
	"k8s.io/apiserver/pkg/storage/names"

	apiservervalidation "k8s.io/apiextensions-apiserver/pkg/apiserver/validation"
)

type customResourceDefinitionStorageStrategy struct {
	runtime.ObjectTyper
	names.NameGenerator

	namespaceScoped bool
	validator       customResourceValidator
}

func NewStrategy(typer runtime.ObjectTyper, namespaceScoped bool, kind schema.GroupVersionKind, validator *validate.SchemaValidator) customResourceDefinitionStorageStrategy {
	return customResourceDefinitionStorageStrategy{
		ObjectTyper:     typer,
		NameGenerator:   names.SimpleNameGenerator,
		namespaceScoped: namespaceScoped,
		validator: customResourceValidator{
			namespaceScoped: namespaceScoped,
			kind:            kind,
			validator:       validator,
		},
	}
}

func (a customResourceDefinitionStorageStrategy) NamespaceScoped() bool {
	return a.namespaceScoped
}

func (customResourceDefinitionStorageStrategy) PrepareForCreate(ctx genericapirequest.Context, obj runtime.Object) {
}

func (customResourceDefinitionStorageStrategy) PrepareForUpdate(ctx genericapirequest.Context, obj, old runtime.Object) {
}

func (a customResourceDefinitionStorageStrategy) Validate(ctx genericapirequest.Context, obj runtime.Object) field.ErrorList {
	return a.validator.Validate(ctx, obj)
}

func (customResourceDefinitionStorageStrategy) AllowCreateOnUpdate() bool {
	return false
}

func (customResourceDefinitionStorageStrategy) AllowUnconditionalUpdate() bool {
	return false
}

func (customResourceDefinitionStorageStrategy) Canonicalize(obj runtime.Object) {
}

func (a customResourceDefinitionStorageStrategy) ValidateUpdate(ctx genericapirequest.Context, obj, old runtime.Object) field.ErrorList {
	return a.validator.ValidateUpdate(ctx, obj, old)
}

func (a customResourceDefinitionStorageStrategy) GetAttrs(obj runtime.Object) (labels.Set, fields.Set, bool, error) {
	accessor, err := meta.Accessor(obj)
	if err != nil {
		return nil, nil, false, err
	}
	return labels.Set(accessor.GetLabels()), objectMetaFieldsSet(accessor, a.namespaceScoped), accessor.GetInitializers() != nil, nil
}

// objectMetaFieldsSet returns a fields that represent the ObjectMeta.
func objectMetaFieldsSet(objectMeta metav1.Object, namespaceScoped bool) fields.Set {
	if namespaceScoped {
		return fields.Set{
			"metadata.name":      objectMeta.GetName(),
			"metadata.namespace": objectMeta.GetNamespace(),
		}
	}
	return fields.Set{
		"metadata.name": objectMeta.GetName(),
	}
}

func (a customResourceDefinitionStorageStrategy) MatchCustomResourceDefinitionStorage(label labels.Selector, field fields.Selector) storage.SelectionPredicate {
	return storage.SelectionPredicate{
		Label:    label,
		Field:    field,
		GetAttrs: a.GetAttrs,
	}
}

type customResourceValidator struct {
	namespaceScoped bool
	kind            schema.GroupVersionKind
	validator       *validate.SchemaValidator
}

func (a customResourceValidator) Validate(ctx genericapirequest.Context, obj runtime.Object) field.ErrorList {
	accessor, err := meta.Accessor(obj)
	if err != nil {
		return field.ErrorList{field.Invalid(field.NewPath("metadata"), nil, err.Error())}
	}
	typeAccessor, err := meta.TypeAccessor(obj)
	if err != nil {
		return field.ErrorList{field.Invalid(field.NewPath("kind"), nil, err.Error())}
	}
	if typeAccessor.GetKind() != a.kind.Kind {
		return field.ErrorList{field.Invalid(field.NewPath("kind"), typeAccessor.GetKind(), fmt.Sprintf("must be %v", a.kind.Kind))}
	}
	if typeAccessor.GetAPIVersion() != a.kind.Group+"/"+a.kind.Version {
		return field.ErrorList{field.Invalid(field.NewPath("apiVersion"), typeAccessor.GetKind(), fmt.Sprintf("must be %v", a.kind.Group+"/"+a.kind.Version))}
	}

	customResourceObject, ok := obj.(*unstructured.Unstructured)
	// this will never happen.
	if !ok {
		return field.ErrorList{field.Invalid(field.NewPath(""), customResourceObject, fmt.Sprintf("has type %T. Must be a pointer to an Unstructured type", customResourceObject))}
	}

	customResource := customResourceObject.UnstructuredContent()
	if err = apiservervalidation.ValidateCustomResource(customResource, a.validator); err != nil {
		return field.ErrorList{field.Invalid(field.NewPath(""), customResource, err.Error())}
	}

	return validation.ValidateObjectMetaAccessor(accessor, a.namespaceScoped, validation.NameIsDNSSubdomain, field.NewPath("metadata"))
}

func (a customResourceValidator) ValidateUpdate(ctx genericapirequest.Context, obj, old runtime.Object) field.ErrorList {
	objAccessor, err := meta.Accessor(obj)
	if err != nil {
		return field.ErrorList{field.Invalid(field.NewPath("metadata"), nil, err.Error())}
	}
	oldAccessor, err := meta.Accessor(old)
	if err != nil {
		return field.ErrorList{field.Invalid(field.NewPath("metadata"), nil, err.Error())}
	}
	typeAccessor, err := meta.TypeAccessor(obj)
	if err != nil {
		return field.ErrorList{field.Invalid(field.NewPath("kind"), nil, err.Error())}
	}
	if typeAccessor.GetKind() != a.kind.Kind {
		return field.ErrorList{field.Invalid(field.NewPath("kind"), typeAccessor.GetKind(), fmt.Sprintf("must be %v", a.kind.Kind))}
	}
	if typeAccessor.GetAPIVersion() != a.kind.Group+"/"+a.kind.Version {
		return field.ErrorList{field.Invalid(field.NewPath("apiVersion"), typeAccessor.GetKind(), fmt.Sprintf("must be %v", a.kind.Group+"/"+a.kind.Version))}
	}

	customResourceObject, ok := obj.(*unstructured.Unstructured)
	// this will never happen.
	if !ok {
		return field.ErrorList{field.Invalid(field.NewPath(""), customResourceObject, fmt.Sprintf("has type %T. Must be a pointer to an Unstructured type", customResourceObject))}
	}

	customResource := customResourceObject.UnstructuredContent()
	if err = apiservervalidation.ValidateCustomResource(customResource, a.validator); err != nil {
		return field.ErrorList{field.Invalid(field.NewPath(""), customResource, err.Error())}
	}

	return validation.ValidateObjectMetaAccessorUpdate(objAccessor, oldAccessor, field.NewPath("metadata"))
}
<commit_msg>fix error message of custrom resource validation<commit_after>/*
Copyright 2017 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 customresource

import (
	"fmt"

	"github.com/go-openapi/validate"

	"k8s.io/apimachinery/pkg/api/meta"
	"k8s.io/apimachinery/pkg/api/validation"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/fields"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/util/validation/field"
	genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
	"k8s.io/apiserver/pkg/storage"
	"k8s.io/apiserver/pkg/storage/names"

	apiservervalidation "k8s.io/apiextensions-apiserver/pkg/apiserver/validation"
)

type customResourceDefinitionStorageStrategy struct {
	runtime.ObjectTyper
	names.NameGenerator

	namespaceScoped bool
	validator       customResourceValidator
}

func NewStrategy(typer runtime.ObjectTyper, namespaceScoped bool, kind schema.GroupVersionKind, validator *validate.SchemaValidator) customResourceDefinitionStorageStrategy {
	return customResourceDefinitionStorageStrategy{
		ObjectTyper:     typer,
		NameGenerator:   names.SimpleNameGenerator,
		namespaceScoped: namespaceScoped,
		validator: customResourceValidator{
			namespaceScoped: namespaceScoped,
			kind:            kind,
			validator:       validator,
		},
	}
}

func (a customResourceDefinitionStorageStrategy) NamespaceScoped() bool {
	return a.namespaceScoped
}

func (customResourceDefinitionStorageStrategy) PrepareForCreate(ctx genericapirequest.Context, obj runtime.Object) {
}

func (customResourceDefinitionStorageStrategy) PrepareForUpdate(ctx genericapirequest.Context, obj, old runtime.Object) {
}

func (a customResourceDefinitionStorageStrategy) Validate(ctx genericapirequest.Context, obj runtime.Object) field.ErrorList {
	return a.validator.Validate(ctx, obj)
}

func (customResourceDefinitionStorageStrategy) AllowCreateOnUpdate() bool {
	return false
}

func (customResourceDefinitionStorageStrategy) AllowUnconditionalUpdate() bool {
	return false
}

func (customResourceDefinitionStorageStrategy) Canonicalize(obj runtime.Object) {
}

func (a customResourceDefinitionStorageStrategy) ValidateUpdate(ctx genericapirequest.Context, obj, old runtime.Object) field.ErrorList {
	return a.validator.ValidateUpdate(ctx, obj, old)
}

func (a customResourceDefinitionStorageStrategy) GetAttrs(obj runtime.Object) (labels.Set, fields.Set, bool, error) {
	accessor, err := meta.Accessor(obj)
	if err != nil {
		return nil, nil, false, err
	}
	return labels.Set(accessor.GetLabels()), objectMetaFieldsSet(accessor, a.namespaceScoped), accessor.GetInitializers() != nil, nil
}

// objectMetaFieldsSet returns a fields that represent the ObjectMeta.
func objectMetaFieldsSet(objectMeta metav1.Object, namespaceScoped bool) fields.Set {
	if namespaceScoped {
		return fields.Set{
			"metadata.name":      objectMeta.GetName(),
			"metadata.namespace": objectMeta.GetNamespace(),
		}
	}
	return fields.Set{
		"metadata.name": objectMeta.GetName(),
	}
}

func (a customResourceDefinitionStorageStrategy) MatchCustomResourceDefinitionStorage(label labels.Selector, field fields.Selector) storage.SelectionPredicate {
	return storage.SelectionPredicate{
		Label:    label,
		Field:    field,
		GetAttrs: a.GetAttrs,
	}
}

type customResourceValidator struct {
	namespaceScoped bool
	kind            schema.GroupVersionKind
	validator       *validate.SchemaValidator
}

func (a customResourceValidator) Validate(ctx genericapirequest.Context, obj runtime.Object) field.ErrorList {
	accessor, err := meta.Accessor(obj)
	if err != nil {
		return field.ErrorList{field.Invalid(field.NewPath("metadata"), nil, err.Error())}
	}
	typeAccessor, err := meta.TypeAccessor(obj)
	if err != nil {
		return field.ErrorList{field.Invalid(field.NewPath("kind"), nil, err.Error())}
	}
	if typeAccessor.GetKind() != a.kind.Kind {
		return field.ErrorList{field.Invalid(field.NewPath("kind"), typeAccessor.GetKind(), fmt.Sprintf("must be %v", a.kind.Kind))}
	}
	if typeAccessor.GetAPIVersion() != a.kind.Group+"/"+a.kind.Version {
		return field.ErrorList{field.Invalid(field.NewPath("apiVersion"), typeAccessor.GetAPIVersion(), fmt.Sprintf("must be %v", a.kind.Group+"/"+a.kind.Version))}
	}

	customResourceObject, ok := obj.(*unstructured.Unstructured)
	// this will never happen.
	if !ok {
		return field.ErrorList{field.Invalid(field.NewPath(""), customResourceObject, fmt.Sprintf("has type %T. Must be a pointer to an Unstructured type", customResourceObject))}
	}

	customResource := customResourceObject.UnstructuredContent()
	if err = apiservervalidation.ValidateCustomResource(customResource, a.validator); err != nil {
		return field.ErrorList{field.Invalid(field.NewPath(""), customResource, err.Error())}
	}

	return validation.ValidateObjectMetaAccessor(accessor, a.namespaceScoped, validation.NameIsDNSSubdomain, field.NewPath("metadata"))
}

func (a customResourceValidator) ValidateUpdate(ctx genericapirequest.Context, obj, old runtime.Object) field.ErrorList {
	objAccessor, err := meta.Accessor(obj)
	if err != nil {
		return field.ErrorList{field.Invalid(field.NewPath("metadata"), nil, err.Error())}
	}
	oldAccessor, err := meta.Accessor(old)
	if err != nil {
		return field.ErrorList{field.Invalid(field.NewPath("metadata"), nil, err.Error())}
	}
	typeAccessor, err := meta.TypeAccessor(obj)
	if err != nil {
		return field.ErrorList{field.Invalid(field.NewPath("kind"), nil, err.Error())}
	}
	if typeAccessor.GetKind() != a.kind.Kind {
		return field.ErrorList{field.Invalid(field.NewPath("kind"), typeAccessor.GetKind(), fmt.Sprintf("must be %v", a.kind.Kind))}
	}
	if typeAccessor.GetAPIVersion() != a.kind.Group+"/"+a.kind.Version {
		return field.ErrorList{field.Invalid(field.NewPath("apiVersion"), typeAccessor.GetAPIVersion(), fmt.Sprintf("must be %v", a.kind.Group+"/"+a.kind.Version))}
	}

	customResourceObject, ok := obj.(*unstructured.Unstructured)
	// this will never happen.
	if !ok {
		return field.ErrorList{field.Invalid(field.NewPath(""), customResourceObject, fmt.Sprintf("has type %T. Must be a pointer to an Unstructured type", customResourceObject))}
	}

	customResource := customResourceObject.UnstructuredContent()
	if err = apiservervalidation.ValidateCustomResource(customResource, a.validator); err != nil {
		return field.ErrorList{field.Invalid(field.NewPath(""), customResource, err.Error())}
	}

	return validation.ValidateObjectMetaAccessorUpdate(objAccessor, oldAccessor, field.NewPath("metadata"))
}
<|endoftext|>


<commit_before>package api

import (
	"encoding/json"
	"fmt"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"runtime"
	"time"

	"github.com/dghubble/sessions"
	"github.com/kardianos/osext"

	"github.com/coreos/tectonic-installer/installer/pkg/terraform"
)

const (
	bcryptCost = 12
)

// TerraformApplyHandlerInput describes the input expected by the
// terraformApplyHandler HTTP Handler.
type TerraformApplyHandlerInput struct {
	Platform      string                 `json:"platform"`
	Credentials   terraform.Credentials  `json:"credentials"`
	AdminPassword []byte                 `json:"adminPassword"`
	Variables     map[string]interface{} `json:"variables"`
	License       string                 `json:"license"`
	PullSecret    string                 `json:"pullSecret"`
	DryRun        bool                   `json:"dryRun"`
	Retry         bool                   `json:"retry"`
}

func terraformApplyHandler(w http.ResponseWriter, req *http.Request, ctx *Context) error {
	// Read the input from the request's body.
	var input TerraformApplyHandlerInput
	if err := json.NewDecoder(req.Body).Decode(&input); err != nil {
		return newBadRequestError("Could not unmarshal input: %s", err)
	}
	defer req.Body.Close()

	var ex *terraform.Executor
	var err error
	if input.Retry {
		// Restore the execution environment from the session.
		_, ex, _, err = restoreExecutionFromSession(req, ctx.Sessions, &input.Credentials)
	} else {
		// Create a new Terraform Executor with the TF variables.
		ex, err = newExecutorFromApplyHandlerInput(&input)
	}
	if err != nil {
		return err
	}
	tfMainDir := fmt.Sprintf("%s/platforms/%s", ex.WorkingDirectory(), input.Platform)

	// Copy the TF Templates to the Executor's working directory.
	if err := terraform.RestoreSources(ex.WorkingDirectory()); err != nil {
		return newInternalServerError("could not write Terraform templates: %s", err)
	}

	// Execute Terraform init and wait for it to finish.
	prepCommand := "init"
	_, prepDone, err := ex.Execute(prepCommand, "-no-color", tfMainDir)
	if err != nil {
		return newInternalServerError("Failed to run Terraform (%s): %s", prepCommand, err)
	}
	<-prepDone

	// Store both the path to the Executor and the ID of the execution so that
	// the status can be read later on.
	session := ctx.Sessions.New(installerSessionName)
	session.Values["terraform_path"] = ex.WorkingDirectory()

	var id int
	var action string
	if input.DryRun {
		id, _, err = ex.Execute("plan", "-no-color", tfMainDir)
		action = "show"
	} else {
		id, _, err = ex.Execute("apply", "-input=false", "-no-color", "-auto-approve", tfMainDir)
		action = "apply"
	}
	if err != nil {
		return newInternalServerError("Failed to run Terraform (%s): %s", action, err)
	}
	session.Values["terraform_id"] = id
	session.Values["action"] = action

	if err := ctx.Sessions.Save(w, session); err != nil {
		return newInternalServerError("Failed to save session: %s", err)
	}

	return nil
}

func terraformAssetsHandler(w http.ResponseWriter, req *http.Request, ctx *Context) error {
	// Restore the execution environment from the session.
	_, ex, _, err := restoreExecutionFromSession(req, ctx.Sessions, nil)
	if err != nil {
		return err
	}

	// Stream the assets as a ZIP.
	w.Header().Set("Content-Type", "application/zip")
	if err := ex.Zip(w, true); err != nil {
		return newInternalServerError("Could not archive assets: %s", err)
	}
	return nil
}

// TerraformDestroyHandlerInput describes the input expected by the
// terraformDestroyHandler HTTP Handler.
type TerraformDestroyHandlerInput struct {
	Platform    string                `json:"platform"`
	Credentials terraform.Credentials `json:"credentials"`
}

func terraformDestroyHandler(w http.ResponseWriter, req *http.Request, ctx *Context) error {
	// Read the input from the request's body.
	var input TerraformDestroyHandlerInput
	if err := json.NewDecoder(req.Body).Decode(&input); err != nil {
		return newBadRequestError("Could not unmarshal input: %s", err)
	}
	defer req.Body.Close()

	// Restore the execution environment from the session.
	_, ex, _, err := restoreExecutionFromSession(req, ctx.Sessions, &input.Credentials)
	if err != nil {
		return err
	}
	tfMainDir := fmt.Sprintf("%s/platforms/%s", ex.WorkingDirectory(), input.Platform)

	// Execute Terraform apply in the background.
	id, _, err := ex.Execute("destroy", "-force", "-no-color", tfMainDir)
	if err != nil {
		return newInternalServerError("Failed to run Terraform (apply): %s", err)
	}

	// Store both the path to the Executor and the ID of the execution so that
	// the status can be read later on.
	session := ctx.Sessions.New(installerSessionName)
	session.Values["action"] = "destroy"
	session.Values["terraform_path"] = ex.WorkingDirectory()
	session.Values["terraform_id"] = id
	if err := ctx.Sessions.Save(w, session); err != nil {
		return newInternalServerError("Failed to save session: %s", err)
	}
	return nil
}

// newExecutorFromApplyHandlerInput creates a new Executor based on the given
// TerraformApplyHandlerInput.
func newExecutorFromApplyHandlerInput(input *TerraformApplyHandlerInput) (*terraform.Executor, error) {
	// Construct the path where the Executor should run based on the the cluster
	// name and current's binary path.
	binaryPath, err := osext.ExecutableFolder()
	if err != nil {
		return nil, newInternalServerError("Could not determine executable's folder: %s", err)
	}
	clusterName := input.Variables["tectonic_cluster_name"].(string)
	if len(clusterName) == 0 {
		return nil, newBadRequestError("Tectonic cluster name not provided")
	}
	exPath := filepath.Join(binaryPath, "clusters", clusterName+time.Now().Format("_2006-01-02_15-04-05"))

	// Publish custom providers to execution environment
	clusterPluginDir := filepath.Join(
		exPath,
		"terraform.d",
		"plugins",
		fmt.Sprintf("%s_%s", runtime.GOOS, runtime.GOARCH),
	)

	err = os.MkdirAll(clusterPluginDir, os.ModeDir|0755)
	if err != nil {
		return nil, newInternalServerError("Could not create custom provider plugins location: %s", err)
	}
	customPlugins := []string{}
	customPlugins, err = filepath.Glob(path.Join(binaryPath, "terraform-provider-*"))
	if err != nil {
		return nil, newInternalServerError("Could not locate custom provider plugins: %s", err)
	}
	for _, pluginBinPath := range customPlugins {
		pluginBin := filepath.Base(pluginBinPath)
		os.Symlink(pluginBinPath, filepath.Join(clusterPluginDir, pluginBin))
	}

	// Create a new Executor.
	ex, err := terraform.NewExecutor(exPath)
	if err != nil {
		return nil, newInternalServerError("Could not create Terraform executor: %s", err)
	}
	// Write the License and Pull Secret to disk, and wire these files in the
	// variables.
	if input.License == "" {
		return nil, newBadRequestError("Tectonic license not provided")
	}
	ex.AddFile("license.txt", []byte(input.License))
	if input.PullSecret == "" {
		return nil, newBadRequestError("Tectonic pull secret not provided")
	}
	ex.AddFile("pull_secret.json", []byte(input.PullSecret))
	input.Variables["tectonic_license_path"] = "./license.txt"
	input.Variables["tectonic_pull_secret_path"] = "./pull_secret.json"

	// Add variables and the required environment variables.
	if variables, err := json.MarshalIndent(input.Variables, "", "  "); err == nil {
		ex.AddVariables(variables)
	} else {
		return nil, newBadRequestError("Could not marshal Terraform variables: %s", err)
	}
	if err := ex.AddCredentials(&input.Credentials); err != nil {
		return nil, newBadRequestError("Could not validate Terraform credentials: %v", err)
	}

	return ex, nil
}

// restoreExecutionFromSession tries to re-create an existing Executor based on
// the data available in session and the provided credentials.
func restoreExecutionFromSession(req *http.Request, sessionProvider sessions.Store, credentials *terraform.Credentials) (*sessions.Session, *terraform.Executor, int, error) {
	session, err := sessionProvider.Get(req, installerSessionName)
	if err != nil {
		return nil, nil, -1, newNotFoundError("Could not find session data. Run terraform apply first.")
	}
	executionPath, ok := session.Values["terraform_path"]
	if !ok {
		return nil, nil, -1, newNotFoundError("Could not find terraform_path in session. Run terraform apply first.")
	}
	executionID, ok := session.Values["terraform_id"]
	if !ok {
		return nil, nil, -1, newNotFoundError("Could not find terraform_id in session. Run terraform apply first.")
	}
	ex, err := terraform.NewExecutor(executionPath.(string))
	if err != nil {
		return nil, nil, -1, newInternalServerError("Could not create Terraform executor")
	}
	if err := ex.AddCredentials(credentials); err != nil {
		return nil, nil, -1, newBadRequestError("Could not validate Terraform credentials")
	}
	return session, ex, executionID.(int), nil
}
<commit_msg>api: Remove unused const bcryptCost<commit_after>package api

import (
	"encoding/json"
	"fmt"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"runtime"
	"time"

	"github.com/dghubble/sessions"
	"github.com/kardianos/osext"

	"github.com/coreos/tectonic-installer/installer/pkg/terraform"
)

// TerraformApplyHandlerInput describes the input expected by the
// terraformApplyHandler HTTP Handler.
type TerraformApplyHandlerInput struct {
	Platform      string                 `json:"platform"`
	Credentials   terraform.Credentials  `json:"credentials"`
	AdminPassword []byte                 `json:"adminPassword"`
	Variables     map[string]interface{} `json:"variables"`
	License       string                 `json:"license"`
	PullSecret    string                 `json:"pullSecret"`
	DryRun        bool                   `json:"dryRun"`
	Retry         bool                   `json:"retry"`
}

func terraformApplyHandler(w http.ResponseWriter, req *http.Request, ctx *Context) error {
	// Read the input from the request's body.
	var input TerraformApplyHandlerInput
	if err := json.NewDecoder(req.Body).Decode(&input); err != nil {
		return newBadRequestError("Could not unmarshal input: %s", err)
	}
	defer req.Body.Close()

	var ex *terraform.Executor
	var err error
	if input.Retry {
		// Restore the execution environment from the session.
		_, ex, _, err = restoreExecutionFromSession(req, ctx.Sessions, &input.Credentials)
	} else {
		// Create a new Terraform Executor with the TF variables.
		ex, err = newExecutorFromApplyHandlerInput(&input)
	}
	if err != nil {
		return err
	}
	tfMainDir := fmt.Sprintf("%s/platforms/%s", ex.WorkingDirectory(), input.Platform)

	// Copy the TF Templates to the Executor's working directory.
	if err := terraform.RestoreSources(ex.WorkingDirectory()); err != nil {
		return newInternalServerError("could not write Terraform templates: %s", err)
	}

	// Execute Terraform init and wait for it to finish.
	prepCommand := "init"
	_, prepDone, err := ex.Execute(prepCommand, "-no-color", tfMainDir)
	if err != nil {
		return newInternalServerError("Failed to run Terraform (%s): %s", prepCommand, err)
	}
	<-prepDone

	// Store both the path to the Executor and the ID of the execution so that
	// the status can be read later on.
	session := ctx.Sessions.New(installerSessionName)
	session.Values["terraform_path"] = ex.WorkingDirectory()

	var id int
	var action string
	if input.DryRun {
		id, _, err = ex.Execute("plan", "-no-color", tfMainDir)
		action = "show"
	} else {
		id, _, err = ex.Execute("apply", "-input=false", "-no-color", "-auto-approve", tfMainDir)
		action = "apply"
	}
	if err != nil {
		return newInternalServerError("Failed to run Terraform (%s): %s", action, err)
	}
	session.Values["terraform_id"] = id
	session.Values["action"] = action

	if err := ctx.Sessions.Save(w, session); err != nil {
		return newInternalServerError("Failed to save session: %s", err)
	}

	return nil
}

func terraformAssetsHandler(w http.ResponseWriter, req *http.Request, ctx *Context) error {
	// Restore the execution environment from the session.
	_, ex, _, err := restoreExecutionFromSession(req, ctx.Sessions, nil)
	if err != nil {
		return err
	}

	// Stream the assets as a ZIP.
	w.Header().Set("Content-Type", "application/zip")
	if err := ex.Zip(w, true); err != nil {
		return newInternalServerError("Could not archive assets: %s", err)
	}
	return nil
}

// TerraformDestroyHandlerInput describes the input expected by the
// terraformDestroyHandler HTTP Handler.
type TerraformDestroyHandlerInput struct {
	Platform    string                `json:"platform"`
	Credentials terraform.Credentials `json:"credentials"`
}

func terraformDestroyHandler(w http.ResponseWriter, req *http.Request, ctx *Context) error {
	// Read the input from the request's body.
	var input TerraformDestroyHandlerInput
	if err := json.NewDecoder(req.Body).Decode(&input); err != nil {
		return newBadRequestError("Could not unmarshal input: %s", err)
	}
	defer req.Body.Close()

	// Restore the execution environment from the session.
	_, ex, _, err := restoreExecutionFromSession(req, ctx.Sessions, &input.Credentials)
	if err != nil {
		return err
	}
	tfMainDir := fmt.Sprintf("%s/platforms/%s", ex.WorkingDirectory(), input.Platform)

	// Execute Terraform apply in the background.
	id, _, err := ex.Execute("destroy", "-force", "-no-color", tfMainDir)
	if err != nil {
		return newInternalServerError("Failed to run Terraform (apply): %s", err)
	}

	// Store both the path to the Executor and the ID of the execution so that
	// the status can be read later on.
	session := ctx.Sessions.New(installerSessionName)
	session.Values["action"] = "destroy"
	session.Values["terraform_path"] = ex.WorkingDirectory()
	session.Values["terraform_id"] = id
	if err := ctx.Sessions.Save(w, session); err != nil {
		return newInternalServerError("Failed to save session: %s", err)
	}
	return nil
}

// newExecutorFromApplyHandlerInput creates a new Executor based on the given
// TerraformApplyHandlerInput.
func newExecutorFromApplyHandlerInput(input *TerraformApplyHandlerInput) (*terraform.Executor, error) {
	// Construct the path where the Executor should run based on the the cluster
	// name and current's binary path.
	binaryPath, err := osext.ExecutableFolder()
	if err != nil {
		return nil, newInternalServerError("Could not determine executable's folder: %s", err)
	}
	clusterName := input.Variables["tectonic_cluster_name"].(string)
	if len(clusterName) == 0 {
		return nil, newBadRequestError("Tectonic cluster name not provided")
	}
	exPath := filepath.Join(binaryPath, "clusters", clusterName+time.Now().Format("_2006-01-02_15-04-05"))

	// Publish custom providers to execution environment
	clusterPluginDir := filepath.Join(
		exPath,
		"terraform.d",
		"plugins",
		fmt.Sprintf("%s_%s", runtime.GOOS, runtime.GOARCH),
	)

	err = os.MkdirAll(clusterPluginDir, os.ModeDir|0755)
	if err != nil {
		return nil, newInternalServerError("Could not create custom provider plugins location: %s", err)
	}
	customPlugins := []string{}
	customPlugins, err = filepath.Glob(path.Join(binaryPath, "terraform-provider-*"))
	if err != nil {
		return nil, newInternalServerError("Could not locate custom provider plugins: %s", err)
	}
	for _, pluginBinPath := range customPlugins {
		pluginBin := filepath.Base(pluginBinPath)
		os.Symlink(pluginBinPath, filepath.Join(clusterPluginDir, pluginBin))
	}

	// Create a new Executor.
	ex, err := terraform.NewExecutor(exPath)
	if err != nil {
		return nil, newInternalServerError("Could not create Terraform executor: %s", err)
	}
	// Write the License and Pull Secret to disk, and wire these files in the
	// variables.
	if input.License == "" {
		return nil, newBadRequestError("Tectonic license not provided")
	}
	ex.AddFile("license.txt", []byte(input.License))
	if input.PullSecret == "" {
		return nil, newBadRequestError("Tectonic pull secret not provided")
	}
	ex.AddFile("pull_secret.json", []byte(input.PullSecret))
	input.Variables["tectonic_license_path"] = "./license.txt"
	input.Variables["tectonic_pull_secret_path"] = "./pull_secret.json"

	// Add variables and the required environment variables.
	if variables, err := json.MarshalIndent(input.Variables, "", "  "); err == nil {
		ex.AddVariables(variables)
	} else {
		return nil, newBadRequestError("Could not marshal Terraform variables: %s", err)
	}
	if err := ex.AddCredentials(&input.Credentials); err != nil {
		return nil, newBadRequestError("Could not validate Terraform credentials: %v", err)
	}

	return ex, nil
}

// restoreExecutionFromSession tries to re-create an existing Executor based on
// the data available in session and the provided credentials.
func restoreExecutionFromSession(req *http.Request, sessionProvider sessions.Store, credentials *terraform.Credentials) (*sessions.Session, *terraform.Executor, int, error) {
	session, err := sessionProvider.Get(req, installerSessionName)
	if err != nil {
		return nil, nil, -1, newNotFoundError("Could not find session data. Run terraform apply first.")
	}
	executionPath, ok := session.Values["terraform_path"]
	if !ok {
		return nil, nil, -1, newNotFoundError("Could not find terraform_path in session. Run terraform apply first.")
	}
	executionID, ok := session.Values["terraform_id"]
	if !ok {
		return nil, nil, -1, newNotFoundError("Could not find terraform_id in session. Run terraform apply first.")
	}
	ex, err := terraform.NewExecutor(executionPath.(string))
	if err != nil {
		return nil, nil, -1, newInternalServerError("Could not create Terraform executor")
	}
	if err := ex.AddCredentials(credentials); err != nil {
		return nil, nil, -1, newBadRequestError("Could not validate Terraform credentials")
	}
	return session, ex, executionID.(int), nil
}
<|endoftext|>


<commit_before>package greq

import (
	"bytes"
	"context"
	"encoding/base64"
	"errors"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"net/http/httptrace"
	"net/url"
	"os"
	"strings"
	"sync"
	"time"

	log "github.com/sirupsen/logrus"

	requestwork "github.com/syhlion/requestwork.v2"
)

type Trace struct {
	Url              string        `json:"url"`
	Method           string        `json:"method"`
	Body             string        `json:"body"`
	Param            string        `json:"param"`
	DNSLookup        time.Duration `json:"dns_lookup"`
	TCPConnection    time.Duration `json:"tcp_connection"`
	TLSHandshake     time.Duration `json:"tls_handshake"`
	ServerProcessing time.Duration `json:"server_prcoessing"`
	ContentTransfer  time.Duration `json:"content_transfer"`
	NameLookup       time.Duration `json:"name_lookup"`
	Connect          time.Duration `json:"connect"`
	PreTransfer      time.Duration `json:"pre_transfer"`
	StartTransfer    time.Duration `json:"start_transfer"`
	Total            time.Duration `json:"total"`
}

var ip string

func getExternalIP() (string, error) {
	ifaces, err := net.Interfaces()
	if err != nil {
		return "", err
	}
	for _, iface := range ifaces {
		if iface.Flags&net.FlagUp == 0 {
			continue // interface down
		}
		if iface.Flags&net.FlagLoopback != 0 {
			continue // loopback interface
		}
		addrs, err := iface.Addrs()
		if err != nil {
			return "", err
		}
		for _, addr := range addrs {
			var ip net.IP
			switch v := addr.(type) {
			case *net.IPNet:
				ip = v.IP
			case *net.IPAddr:
				ip = v.IP
			}
			if ip == nil || ip.IsLoopback() {
				continue
			}
			ip = ip.To4()
			if ip == nil {
				continue // not an ipv4 address
			}
			return ip.String(), nil
		}
	}
	return "", errors.New("are you connected to the network?")
}

func init() {
	log.SetFormatter(&log.JSONFormatter{})

	log.SetOutput(os.Stdout)
	log.SetLevel(log.DebugLevel)
	ip, _ = getExternalIP()
}

//New return http client
func New(worker *requestwork.Worker, timeout time.Duration, debug bool) *Client {
	return &Client{
		worker:  worker,
		timeout: timeout,
		headers: make(map[string]string),
		lock:    &sync.RWMutex{},
		debug:   debug,
	}
}

//Client instance
type Client struct {
	worker  *requestwork.Worker
	timeout time.Duration
	headers map[string]string
	host    string
	lock    *sync.RWMutex
	debug   bool
}

//SetBasicAuth  set Basic auth
func (c *Client) SetBasicAuth(username, password string) *Client {
	auth := username + ":" + password
	hash := base64.StdEncoding.EncodeToString([]byte(auth))
	c.lock.Lock()
	defer c.lock.Unlock()
	c.headers["Authorization"] = "Basic " + hash
	return c
}

//SetHeader set http header
func (c *Client) SetHeader(key, value string) *Client {
	key = strings.Title(key)
	c.lock.Lock()
	defer c.lock.Unlock()
	c.headers[key] = value
	return c
}

//SetHost set host
func (c *Client) SetHost(host string) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()

	c.host = host
	return c
}

//Get http method get
func (c *Client) Get(url string, params url.Values) (data []byte, httpstatus int, err error) {
	if params != nil {
		url += "?" + params.Encode()
	}
	req, err := http.NewRequest(http.MethodGet, url, nil)
	return c.resolveRequest(req, params, err)

}
func (c *Client) GetWithOnceHeader(url string, params url.Values, headers map[string]string) (data []byte, httpstatus int, err error) {
	req, err := http.NewRequest(http.MethodPost, url, strings.NewReader(params.Encode()))
	if err != nil {
		return
	}
	for key, value := range headers {
		req.Header.Set(key, value)
	}
	return c.resolveRequest(req, params, err)
}

//Post http method post
func (c *Client) Post(url string, params url.Values) (data []byte, httpstatus int, err error) {
	req, err := http.NewRequest(http.MethodPost, url, strings.NewReader(params.Encode()))
	return c.resolveRequest(req, params, err)
}
func (c *Client) PostRaw(url string, body io.Reader) (data []byte, httpstatus int, err error) {
	req, err := http.NewRequest(http.MethodPost, url, body)
	return c.resolveRawRequest(req, body, err)
}
func (c *Client) PostRawWithOnceHeader(url string, body io.Reader, headers map[string]string) (data []byte, httpstatus int, err error) {
	req, err := http.NewRequest(http.MethodPost, url, body)
	if err != nil {
		return
	}
	for key, value := range headers {
		req.Header.Set(key, value)
	}
	return c.resolveRawRequest(req, body, err)
}

func (c *Client) PostWithOnceHeader(url string, params url.Values, headers map[string]string) (data []byte, httpstatus int, err error) {
	req, err := http.NewRequest(http.MethodPost, url, strings.NewReader(params.Encode()))
	if err != nil {
		return
	}
	for key, value := range headers {
		req.Header.Set(key, value)
	}
	return c.resolveRequest(req, params, err)
}

//Put http method put
func (c *Client) Put(url string, params url.Values) (data []byte, httpstatus int, err error) {
	req, err := http.NewRequest(http.MethodPut, url, strings.NewReader(params.Encode()))
	return c.resolveRequest(req, params, err)
}
func (c *Client) PutWithOnceHeader(url string, params url.Values, headers map[string]string) (data []byte, httpstatus int, err error) {
	req, err := http.NewRequest(http.MethodPost, url, strings.NewReader(params.Encode()))
	if err != nil {
		return
	}
	for key, value := range headers {
		req.Header.Set(key, value)
	}

	return c.resolveRequest(req, params, err)
}

//Delete http method Delete
func (c *Client) Delete(url string, params url.Values) (data []byte, httpstatus int, err error) {
	req, err := http.NewRequest(http.MethodDelete, url, strings.NewReader(params.Encode()))
	return c.resolveRequest(req, params, err)
}
func (c *Client) DeleteWithOnceHeader(url string, params url.Values, headers map[string]string) (data []byte, httpstatus int, err error) {
	req, err := http.NewRequest(http.MethodPost, url, strings.NewReader(params.Encode()))
	if err != nil {
		return
	}
	for key, value := range headers {
		req.Header.Set(key, value)
	}
	return c.resolveRequest(req, params, err)
}

func (c *Client) resolveHeaders(req *http.Request) {
	c.lock.RLock()
	c.lock.RUnlock()
	for key, value := range c.headers {
		req.Header.Set(key, value)
	}
	if c.host != "" {
		req.Host = c.host
	}
}

func (c *Client) resolveRawRequest(req *http.Request, bb io.Reader, e error) (data []byte, httpstatus int, err error) {
	var (
		body           []byte
		status         int
		trace          *httptrace.ClientTrace
		t0, t3, t4, t5 time.Time
	)
	t0 = time.Now()
	if c.debug {
		var stat Trace
		defer func() {
			buf := new(bytes.Buffer)
			buf.ReadFrom(bb)
			stat.Param = buf.String()
			stat.Url = req.URL.String()
			stat.Method = req.Method
			stat.Body = string(data)
			stat.TCPConnection = t3.Sub(t0)
			stat.ServerProcessing = t4.Sub(t3)
			stat.ContentTransfer = t5.Sub(t4)
			stat.Connect = t3.Sub(t0)
			stat.StartTransfer = t4.Sub(t0)
			stat.Total = t5.Sub(t0)
			log.WithFields(log.Fields{
				"ip":     ip,
				"name":   "syhlion/greq",
				"param":  stat.Param,
				"url":    stat.Url,
				"method": stat.Method,
				//	"body":              stat.Body,
				"tcp_connection":    stat.TCPConnection.String(),
				"server_processing": stat.ServerProcessing.String(),
				"content_transfer":  stat.ContentTransfer.String(),
				"connect":           stat.Connect.String(),
				"start_transfer":    stat.StartTransfer.String(),
				"total":             stat.Total.String(),
			}).Debug("http trace")

		}()
		trace = &httptrace.ClientTrace{
			GotConn:              func(_ httptrace.GotConnInfo) { t3 = time.Now() },
			GotFirstResponseByte: func() { t4 = time.Now() },
		}
		req = req.WithContext(httptrace.WithClientTrace(context.Background(), trace))
	}
	if e != nil {
		return
	}
	ctx, cancel := context.WithTimeout(context.Background(), c.timeout)

	defer cancel()
	c.resolveHeaders(req)

	switch req.Method {
	case "PUT", "POST", "DELETE":
		req.Header.Set("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
	}

	err = c.worker.Execute(ctx, req, func(resp *http.Response, err error) (er error) {
		if err != nil {
			return err
		}
		var readErr error
		defer func() {
			resp.Body.Close()
			t5 = time.Now()
		}()
		status = resp.StatusCode
		body, readErr = ioutil.ReadAll(resp.Body)
		if readErr != nil {
			return readErr
		}
		return
	})
	if err != nil {
		return
	}
	data = body
	httpstatus = status
	return

}
func (c *Client) resolveRequest(req *http.Request, params url.Values, e error) (data []byte, httpstatus int, err error) {
	var (
		body           []byte
		status         int
		trace          *httptrace.ClientTrace
		t0, t3, t4, t5 time.Time
	)
	t0 = time.Now()
	if c.debug {
		var stat Trace
		defer func() {
			stat.Param = params.Encode()
			stat.Url = req.URL.String()
			stat.Method = req.Method
			stat.Body = string(data)
			stat.TCPConnection = t3.Sub(t0)
			stat.ServerProcessing = t4.Sub(t3)
			stat.ContentTransfer = t5.Sub(t4)
			stat.Connect = t3.Sub(t0)
			stat.StartTransfer = t4.Sub(t0)
			stat.Total = t5.Sub(t0)
			log.WithFields(log.Fields{
				"ip":     ip,
				"name":   "syhlion/greq",
				"param":  stat.Param,
				"url":    stat.Url,
				"method": stat.Method,
				//	"body":              stat.Body,
				"tcp_connection":    stat.TCPConnection.String(),
				"server_processing": stat.ServerProcessing.String(),
				"content_transfer":  stat.ContentTransfer.String(),
				"connect":           stat.Connect.String(),
				"start_transfer":    stat.StartTransfer.String(),
				"total":             stat.Total.String(),
			}).Debug("http trace")

		}()
		trace = &httptrace.ClientTrace{
			GotConn:              func(_ httptrace.GotConnInfo) { t3 = time.Now() },
			GotFirstResponseByte: func() { t4 = time.Now() },
		}
		req = req.WithContext(httptrace.WithClientTrace(context.Background(), trace))
	}
	if e != nil {
		return
	}
	ctx, cancel := context.WithTimeout(context.Background(), c.timeout)

	defer cancel()
	c.resolveHeaders(req)

	switch req.Method {
	case "PUT", "POST", "DELETE":
		req.Header.Set("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
	}

	err = c.worker.Execute(ctx, req, func(resp *http.Response, err error) (er error) {
		if err != nil {
			return err
		}
		var readErr error
		defer func() {
			resp.Body.Close()
			t5 = time.Now()
		}()
		status = resp.StatusCode
		body, readErr = ioutil.ReadAll(resp.Body)
		if readErr != nil {
			return readErr
		}
		return
	})
	if err != nil {
		return
	}
	data = body
	httpstatus = status
	return

}
<commit_msg>fix post raw header<commit_after>package greq

import (
	"bytes"
	"context"
	"encoding/base64"
	"errors"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"net/http/httptrace"
	"net/url"
	"os"
	"strings"
	"sync"
	"time"

	log "github.com/sirupsen/logrus"

	requestwork "github.com/syhlion/requestwork.v2"
)

type Trace struct {
	Url              string        `json:"url"`
	Method           string        `json:"method"`
	Body             string        `json:"body"`
	Param            string        `json:"param"`
	DNSLookup        time.Duration `json:"dns_lookup"`
	TCPConnection    time.Duration `json:"tcp_connection"`
	TLSHandshake     time.Duration `json:"tls_handshake"`
	ServerProcessing time.Duration `json:"server_prcoessing"`
	ContentTransfer  time.Duration `json:"content_transfer"`
	NameLookup       time.Duration `json:"name_lookup"`
	Connect          time.Duration `json:"connect"`
	PreTransfer      time.Duration `json:"pre_transfer"`
	StartTransfer    time.Duration `json:"start_transfer"`
	Total            time.Duration `json:"total"`
}

var ip string

func getExternalIP() (string, error) {
	ifaces, err := net.Interfaces()
	if err != nil {
		return "", err
	}
	for _, iface := range ifaces {
		if iface.Flags&net.FlagUp == 0 {
			continue // interface down
		}
		if iface.Flags&net.FlagLoopback != 0 {
			continue // loopback interface
		}
		addrs, err := iface.Addrs()
		if err != nil {
			return "", err
		}
		for _, addr := range addrs {
			var ip net.IP
			switch v := addr.(type) {
			case *net.IPNet:
				ip = v.IP
			case *net.IPAddr:
				ip = v.IP
			}
			if ip == nil || ip.IsLoopback() {
				continue
			}
			ip = ip.To4()
			if ip == nil {
				continue // not an ipv4 address
			}
			return ip.String(), nil
		}
	}
	return "", errors.New("are you connected to the network?")
}

func init() {
	log.SetFormatter(&log.JSONFormatter{})

	log.SetOutput(os.Stdout)
	log.SetLevel(log.DebugLevel)
	ip, _ = getExternalIP()
}

//New return http client
func New(worker *requestwork.Worker, timeout time.Duration, debug bool) *Client {
	return &Client{
		worker:  worker,
		timeout: timeout,
		headers: make(map[string]string),
		lock:    &sync.RWMutex{},
		debug:   debug,
	}
}

//Client instance
type Client struct {
	worker  *requestwork.Worker
	timeout time.Duration
	headers map[string]string
	host    string
	lock    *sync.RWMutex
	debug   bool
}

//SetBasicAuth  set Basic auth
func (c *Client) SetBasicAuth(username, password string) *Client {
	auth := username + ":" + password
	hash := base64.StdEncoding.EncodeToString([]byte(auth))
	c.lock.Lock()
	defer c.lock.Unlock()
	c.headers["Authorization"] = "Basic " + hash
	return c
}

//SetHeader set http header
func (c *Client) SetHeader(key, value string) *Client {
	key = strings.Title(key)
	c.lock.Lock()
	defer c.lock.Unlock()
	c.headers[key] = value
	return c
}

//SetHost set host
func (c *Client) SetHost(host string) *Client {
	c.lock.Lock()
	defer c.lock.Unlock()

	c.host = host
	return c
}

//Get http method get
func (c *Client) Get(url string, params url.Values) (data []byte, httpstatus int, err error) {
	if params != nil {
		url += "?" + params.Encode()
	}
	req, err := http.NewRequest(http.MethodGet, url, nil)
	return c.resolveRequest(req, params, err)

}
func (c *Client) GetWithOnceHeader(url string, params url.Values, headers map[string]string) (data []byte, httpstatus int, err error) {
	req, err := http.NewRequest(http.MethodPost, url, strings.NewReader(params.Encode()))
	if err != nil {
		return
	}
	for key, value := range headers {
		req.Header.Set(key, value)
	}
	return c.resolveRequest(req, params, err)
}

//Post http method post
func (c *Client) Post(url string, params url.Values) (data []byte, httpstatus int, err error) {
	req, err := http.NewRequest(http.MethodPost, url, strings.NewReader(params.Encode()))
	return c.resolveRequest(req, params, err)
}
func (c *Client) PostRaw(url string, body io.Reader) (data []byte, httpstatus int, err error) {
	req, err := http.NewRequest(http.MethodPost, url, body)
	return c.resolveRawRequest(req, body, err)
}
func (c *Client) PostRawWithOnceHeader(url string, body io.Reader, headers map[string]string) (data []byte, httpstatus int, err error) {
	req, err := http.NewRequest(http.MethodPost, url, body)
	if err != nil {
		return
	}
	for key, value := range headers {
		req.Header.Set(key, value)
	}
	return c.resolveRawRequest(req, body, err)
}

func (c *Client) PostWithOnceHeader(url string, params url.Values, headers map[string]string) (data []byte, httpstatus int, err error) {
	req, err := http.NewRequest(http.MethodPost, url, strings.NewReader(params.Encode()))
	if err != nil {
		return
	}
	for key, value := range headers {
		req.Header.Set(key, value)
	}
	return c.resolveRequest(req, params, err)
}

//Put http method put
func (c *Client) Put(url string, params url.Values) (data []byte, httpstatus int, err error) {
	req, err := http.NewRequest(http.MethodPut, url, strings.NewReader(params.Encode()))
	return c.resolveRequest(req, params, err)
}
func (c *Client) PutWithOnceHeader(url string, params url.Values, headers map[string]string) (data []byte, httpstatus int, err error) {
	req, err := http.NewRequest(http.MethodPost, url, strings.NewReader(params.Encode()))
	if err != nil {
		return
	}
	for key, value := range headers {
		req.Header.Set(key, value)
	}

	return c.resolveRequest(req, params, err)
}

//Delete http method Delete
func (c *Client) Delete(url string, params url.Values) (data []byte, httpstatus int, err error) {
	req, err := http.NewRequest(http.MethodDelete, url, strings.NewReader(params.Encode()))
	return c.resolveRequest(req, params, err)
}
func (c *Client) DeleteWithOnceHeader(url string, params url.Values, headers map[string]string) (data []byte, httpstatus int, err error) {
	req, err := http.NewRequest(http.MethodPost, url, strings.NewReader(params.Encode()))
	if err != nil {
		return
	}
	for key, value := range headers {
		req.Header.Set(key, value)
	}
	return c.resolveRequest(req, params, err)
}

func (c *Client) resolveHeaders(req *http.Request) {
	c.lock.RLock()
	c.lock.RUnlock()
	for key, value := range c.headers {
		req.Header.Set(key, value)
	}
	if c.host != "" {
		req.Host = c.host
	}
}

func (c *Client) resolveRawRequest(req *http.Request, bb io.Reader, e error) (data []byte, httpstatus int, err error) {
	var (
		body           []byte
		status         int
		trace          *httptrace.ClientTrace
		t0, t3, t4, t5 time.Time
	)
	t0 = time.Now()
	if c.debug {
		var stat Trace
		defer func() {
			buf := new(bytes.Buffer)
			buf.ReadFrom(bb)
			stat.Param = buf.String()
			stat.Url = req.URL.String()
			stat.Method = req.Method
			stat.Body = string(data)
			stat.TCPConnection = t3.Sub(t0)
			stat.ServerProcessing = t4.Sub(t3)
			stat.ContentTransfer = t5.Sub(t4)
			stat.Connect = t3.Sub(t0)
			stat.StartTransfer = t4.Sub(t0)
			stat.Total = t5.Sub(t0)
			log.WithFields(log.Fields{
				"ip":     ip,
				"name":   "syhlion/greq",
				"param":  stat.Param,
				"url":    stat.Url,
				"method": stat.Method,
				//	"body":              stat.Body,
				"tcp_connection":    stat.TCPConnection.String(),
				"server_processing": stat.ServerProcessing.String(),
				"content_transfer":  stat.ContentTransfer.String(),
				"connect":           stat.Connect.String(),
				"start_transfer":    stat.StartTransfer.String(),
				"total":             stat.Total.String(),
			}).Debug("http trace")

		}()
		trace = &httptrace.ClientTrace{
			GotConn:              func(_ httptrace.GotConnInfo) { t3 = time.Now() },
			GotFirstResponseByte: func() { t4 = time.Now() },
		}
		req = req.WithContext(httptrace.WithClientTrace(context.Background(), trace))
	}
	if e != nil {
		return
	}
	ctx, cancel := context.WithTimeout(context.Background(), c.timeout)

	defer cancel()
	c.resolveHeaders(req)

	err = c.worker.Execute(ctx, req, func(resp *http.Response, err error) (er error) {
		if err != nil {
			return err
		}
		var readErr error
		defer func() {
			resp.Body.Close()
			t5 = time.Now()
		}()
		status = resp.StatusCode
		body, readErr = ioutil.ReadAll(resp.Body)
		if readErr != nil {
			return readErr
		}
		return
	})
	if err != nil {
		return
	}
	data = body
	httpstatus = status
	return

}
func (c *Client) resolveRequest(req *http.Request, params url.Values, e error) (data []byte, httpstatus int, err error) {
	var (
		body           []byte
		status         int
		trace          *httptrace.ClientTrace
		t0, t3, t4, t5 time.Time
	)
	t0 = time.Now()
	if c.debug {
		var stat Trace
		defer func() {
			stat.Param = params.Encode()
			stat.Url = req.URL.String()
			stat.Method = req.Method
			stat.Body = string(data)
			stat.TCPConnection = t3.Sub(t0)
			stat.ServerProcessing = t4.Sub(t3)
			stat.ContentTransfer = t5.Sub(t4)
			stat.Connect = t3.Sub(t0)
			stat.StartTransfer = t4.Sub(t0)
			stat.Total = t5.Sub(t0)
			log.WithFields(log.Fields{
				"ip":     ip,
				"name":   "syhlion/greq",
				"param":  stat.Param,
				"url":    stat.Url,
				"method": stat.Method,
				//	"body":              stat.Body,
				"tcp_connection":    stat.TCPConnection.String(),
				"server_processing": stat.ServerProcessing.String(),
				"content_transfer":  stat.ContentTransfer.String(),
				"connect":           stat.Connect.String(),
				"start_transfer":    stat.StartTransfer.String(),
				"total":             stat.Total.String(),
			}).Debug("http trace")

		}()
		trace = &httptrace.ClientTrace{
			GotConn:              func(_ httptrace.GotConnInfo) { t3 = time.Now() },
			GotFirstResponseByte: func() { t4 = time.Now() },
		}
		req = req.WithContext(httptrace.WithClientTrace(context.Background(), trace))
	}
	if e != nil {
		return
	}
	ctx, cancel := context.WithTimeout(context.Background(), c.timeout)

	defer cancel()
	c.resolveHeaders(req)

	switch req.Method {
	case "PUT", "POST", "DELETE":
		req.Header.Set("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
	}

	err = c.worker.Execute(ctx, req, func(resp *http.Response, err error) (er error) {
		if err != nil {
			return err
		}
		var readErr error
		defer func() {
			resp.Body.Close()
			t5 = time.Now()
		}()
		status = resp.StatusCode
		body, readErr = ioutil.ReadAll(resp.Body)
		if readErr != nil {
			return readErr
		}
		return
	})
	if err != nil {
		return
	}
	data = body
	httpstatus = status
	return

}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Skaffold 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 generatepipeline

import (
	"fmt"
	"os"

	"github.com/pkg/errors"

	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/pipeline"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/version"

	tekton "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1"
	corev1 "k8s.io/api/core/v1"
)

func generateBuildTasks(configFiles []*ConfigFile) ([]*tekton.Task, error) {
	var tasks []*tekton.Task
	for i, configFile := range configFiles {
		task, err := generateBuildTask(configFile)
		if err != nil {
			return nil, err
		}
		task.Spec.Steps[0].Name = fmt.Sprintf("%s-%d", task.Spec.Steps[0].Name, i)

		tasks = append(tasks, task)
	}

	return tasks, nil
}

func generateBuildTask(configFile *ConfigFile) (*tekton.Task, error) {
	buildConfig := configFile.Profile.Build
	if len(buildConfig.Artifacts) == 0 {
		return nil, errors.New("no artifacts to build")
	}

	skaffoldVersion := os.Getenv("PIPELINE_SKAFFOLD_VERSION")
	if skaffoldVersion == "" {
		skaffoldVersion = version.Get().Version
	}

	resources := []tekton.TaskResource{
		{
			Name: "source",
			Type: tekton.PipelineResourceTypeGit,
		},
	}
	inputs := &tekton.Inputs{Resources: resources}
	outputs := &tekton.Outputs{Resources: resources}
	steps := []corev1.Container{
		{
			Name:       "run-build",
			Image:      fmt.Sprintf("gcr.io/k8s-skaffold/skaffold:%s", skaffoldVersion),
			WorkingDir: "/workspace/source",
			Command:    []string{"skaffold", "build"},
			Args: []string{
				"--filename", configFile.Path,
				"--profile", "oncluster",
				"--file-output", "build.out",
			},
		},
	}

	// Add secret volume mounting if any artifacts in config need to be built with kaniko
	var volumes []corev1.Volume
	for _, artifact := range buildConfig.Artifacts {
		if artifact.KanikoArtifact != nil {
			volumes = []corev1.Volume{
				{
					Name: kanikoSecretName,
					VolumeSource: corev1.VolumeSource{
						Secret: &corev1.SecretVolumeSource{
							SecretName: kanikoSecretName,
						},
					},
				},
			}
			steps[0].VolumeMounts = []corev1.VolumeMount{
				{
					Name:      kanikoSecretName,
					MountPath: "/secret",
				},
			}
			steps[0].Env = []corev1.EnvVar{
				{
					Name:  "GOOGLE_APPLICATION_CREDENTIALS",
					Value: "/secret/" + kanikoSecretName,
				},
			}
		}
	}

	return pipeline.NewTask("skaffold-build", inputs, outputs, steps, volumes), nil
}

func generateDeployTasks(configFiles []*ConfigFile) ([]*tekton.Task, error) {
	var tasks []*tekton.Task
	for i, configFile := range configFiles {
		task, err := generateDeployTask(configFile)
		if err != nil {
			return nil, err
		}
		task.Spec.Steps[0].Name = fmt.Sprintf("%s-%d", task.Spec.Steps[0].Name, i)

		tasks = append(tasks, task)
	}

	return tasks, nil
}

func generateDeployTask(configFile *ConfigFile) (*tekton.Task, error) {
	deployConfig := configFile.Config.Deploy
	if deployConfig.HelmDeploy == nil && deployConfig.KubectlDeploy == nil && deployConfig.KustomizeDeploy == nil {
		return nil, errors.New("no Helm/Kubectl/Kustomize deploy config")
	}

	skaffoldVersion := os.Getenv("PIPELINE_SKAFFOLD_VERSION")
	if skaffoldVersion == "" {
		skaffoldVersion = version.Get().Version
	}

	resources := []tekton.TaskResource{
		{
			Name: "source",
			Type: tekton.PipelineResourceTypeGit,
		},
	}
	inputs := &tekton.Inputs{Resources: resources}
	steps := []corev1.Container{
		{
			Name:       "run-deploy",
			Image:      fmt.Sprintf("gcr.io/k8s-skaffold/skaffold:%s", skaffoldVersion),
			WorkingDir: "/workspace/source",
			Command:    []string{"skaffold", "deploy"},
			Args: []string{
				"--filename", configFile.Path,
				"--build-artifacts", "build.out",
			},
		},
	}

	return pipeline.NewTask("skaffold-deploy", inputs, nil, steps, nil), nil
}
<commit_msg>change task name, not step name<commit_after>/*
Copyright 2019 The Skaffold 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 generatepipeline

import (
	"fmt"
	"os"

	"github.com/pkg/errors"

	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/pipeline"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/version"

	tekton "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1alpha1"
	corev1 "k8s.io/api/core/v1"
)

func generateBuildTasks(configFiles []*ConfigFile) ([]*tekton.Task, error) {
	var tasks []*tekton.Task
	for i, configFile := range configFiles {
		task, err := generateBuildTask(configFile)
		if err != nil {
			return nil, err
		}
		task.Name = fmt.Sprintf("%s-%d", task.Name, i)

		tasks = append(tasks, task)
	}

	return tasks, nil
}

func generateBuildTask(configFile *ConfigFile) (*tekton.Task, error) {
	buildConfig := configFile.Profile.Build
	if len(buildConfig.Artifacts) == 0 {
		return nil, errors.New("no artifacts to build")
	}

	skaffoldVersion := os.Getenv("PIPELINE_SKAFFOLD_VERSION")
	if skaffoldVersion == "" {
		skaffoldVersion = version.Get().Version
	}

	resources := []tekton.TaskResource{
		{
			Name: "source",
			Type: tekton.PipelineResourceTypeGit,
		},
	}
	inputs := &tekton.Inputs{Resources: resources}
	outputs := &tekton.Outputs{Resources: resources}
	steps := []corev1.Container{
		{
			Name:       "run-build",
			Image:      fmt.Sprintf("gcr.io/k8s-skaffold/skaffold:%s", skaffoldVersion),
			WorkingDir: "/workspace/source",
			Command:    []string{"skaffold", "build"},
			Args: []string{
				"--filename", configFile.Path,
				"--profile", "oncluster",
				"--file-output", "build.out",
			},
		},
	}

	// Add secret volume mounting if any artifacts in config need to be built with kaniko
	var volumes []corev1.Volume
	for _, artifact := range buildConfig.Artifacts {
		if artifact.KanikoArtifact != nil {
			volumes = []corev1.Volume{
				{
					Name: kanikoSecretName,
					VolumeSource: corev1.VolumeSource{
						Secret: &corev1.SecretVolumeSource{
							SecretName: kanikoSecretName,
						},
					},
				},
			}
			steps[0].VolumeMounts = []corev1.VolumeMount{
				{
					Name:      kanikoSecretName,
					MountPath: "/secret",
				},
			}
			steps[0].Env = []corev1.EnvVar{
				{
					Name:  "GOOGLE_APPLICATION_CREDENTIALS",
					Value: "/secret/" + kanikoSecretName,
				},
			}
		}
	}

	return pipeline.NewTask("skaffold-build", inputs, outputs, steps, volumes), nil
}

func generateDeployTasks(configFiles []*ConfigFile) ([]*tekton.Task, error) {
	var tasks []*tekton.Task
	for i, configFile := range configFiles {
		task, err := generateDeployTask(configFile)
		if err != nil {
			return nil, err
		}
		task.Name = fmt.Sprintf("%s-%d", task.Name, i)

		tasks = append(tasks, task)
	}

	return tasks, nil
}

func generateDeployTask(configFile *ConfigFile) (*tekton.Task, error) {
	deployConfig := configFile.Config.Deploy
	if deployConfig.HelmDeploy == nil && deployConfig.KubectlDeploy == nil && deployConfig.KustomizeDeploy == nil {
		return nil, errors.New("no Helm/Kubectl/Kustomize deploy config")
	}

	skaffoldVersion := os.Getenv("PIPELINE_SKAFFOLD_VERSION")
	if skaffoldVersion == "" {
		skaffoldVersion = version.Get().Version
	}

	resources := []tekton.TaskResource{
		{
			Name: "source",
			Type: tekton.PipelineResourceTypeGit,
		},
	}
	inputs := &tekton.Inputs{Resources: resources}
	steps := []corev1.Container{
		{
			Name:       "run-deploy",
			Image:      fmt.Sprintf("gcr.io/k8s-skaffold/skaffold:%s", skaffoldVersion),
			WorkingDir: "/workspace/source",
			Command:    []string{"skaffold", "deploy"},
			Args: []string{
				"--filename", configFile.Path,
				"--build-artifacts", "build.out",
			},
		},
	}

	return pipeline.NewTask("skaffold-deploy", inputs, nil, steps, nil), nil
}
<|endoftext|>


<commit_before>// Copyright (c) 2014 Jason Goecke
// client.go

package wit

import (
	"bytes"
	"errors"
	"io/ioutil"
	"net/http"
	"os"
	"regexp"
)

const (
	// UserAgent is the HTTP Uesr Agent sent on HTTP requests
	UserAgent = "WIT (Go net/http)"
	// APIVersion is the version of the Wit API supported
	APIVersion = "v=20140401"
)

// Client represents a client for the Wit API (https://wit.ai/docs/api)
type Client struct {
	APIBase string
}

// HTTPParams represents the HTTP parameters to pass along to the Wit API
type HTTPParams struct {
	Verb        string
	Resource    string
	ContentType string
	Data        []byte
}

// Stores the ApiKey for the Wit API
var APIKey string

// NewClient creates a new client for the Wit API
//
//		client := wit.NewClient("<ACCESS-TOKEN>")
func NewClient(apiKey string) *Client {
	client := &Client{}
	client.APIBase = "https://api.wit.ai"
	APIKey = apiKey
	return client
}

// Provides a common facility for doing a DELETE on a Wit resource
//
//		result, err := delete("https://api.wit.ai/entities", "favorite_city")
func delete(resource string, id string) ([]byte, error) {
	httpParams := &HTTPParams{}
	httpParams.Resource = resource + "/" + id
	httpParams.Verb = "DELETE"
	return processRequest(httpParams)
}

// Provides a common facility for doing a GET on a Wit resource
//
//		result, err := get("https://api.wit.ai/entities/favorite_city")
func get(resource string) ([]byte, error) {
	httpParams := &HTTPParams{}
	httpParams.Resource = resource
	httpParams.Verb = "GET"
	return processRequest(httpParams)
}

// Provides a common facility for doing a POST on a Wit resource. Takes
// JSON []byte for the data argument.
//
//		result, err := post("https://api.wit.ai/entities", entity)
func post(resource string, data []byte) ([]byte, error) {
	httpParams := &HTTPParams{"POST", resource, "application/json", data}
	return processRequest(httpParams)
}

// Provides a common facility for doing a POST with a file on a Wit resource.
//
//		result, err := postFile("https://api.wit.ai/messages", message)
func postFile(resource string, request *MessageRequest) ([]byte, error) {
	if request.File != "" {
		file, err := os.Open(request.File)
		if err != nil {
			return nil, err
		}
		defer file.Close()
		stats, statsErr := file.Stat()
		if statsErr != nil {
			return nil, statsErr
		}
		size := stats.Size()
		data := make([]byte, size)
		file.Read(data)
		httpParams := &HTTPParams{"POST", resource, request.ContentType, data}
		return processRequest(httpParams)
	}

	if request.FileContents != nil {
		httpParams := &HTTPParams{"POST", resource, request.ContentType, request.FileContents}
		return processRequest(httpParams)
		// } else {
		// return nil, errors.New("Must provide a filename or contents")
	}

	return nil, errors.New("Must provide a filename or contents")
}

// Provides a common facility for doing a PUT on a Wit resource.
//
//		result, err := put("https://api.wit.ai/entities", entity)
func put(resource string, data []byte) ([]byte, error) {
	httpParams := &HTTPParams{"PUT", resource, "application/json", data}
	return processRequest(httpParams)
}

// Processes an HTTP request to the Wit API
func processRequest(httpParams *HTTPParams) ([]byte, error) {
	regex := regexp.MustCompile(`\?`)
	if regex.MatchString(httpParams.Resource) {
		httpParams.Resource += "&" + APIVersion
	} else {
		httpParams.Resource += "?" + APIVersion
	}
	reader := bytes.NewReader(httpParams.Data)
	httpClient := &http.Client{}
	req, err := http.NewRequest(httpParams.Verb, httpParams.Resource, reader)
	if err != nil {
		return nil, err
	}
	setHeaders(req, httpParams.ContentType)
	result, err := httpClient.Do(req)
	if err != nil {
		return nil, err
	}
	body, err := ioutil.ReadAll(result.Body)
	result.Body.Close()
	if result.StatusCode != 200 {
		return nil, errors.New(http.StatusText(result.StatusCode))
	}
	return body, nil
}

// Sets the custom headers required for the Wit.ai API
//
//		setHeaders(req, httpParams.ContentType)
func setHeaders(req *http.Request, contentType string) {
	req.Header.Add("Authorization", "Bearer "+APIKey)
	req.Header.Set("Content-Type", contentType)
	req.Header.Set("Accept", "application/json")
}
<commit_msg>Refactoring for more good Go-ness<commit_after>// Copyright (c) 2014 Jason Goecke
// client.go

package wit

import (
	"bytes"
	"errors"
	"io/ioutil"
	"net/http"
	"os"
	"regexp"
)

const (
	// UserAgent is the HTTP Uesr Agent sent on HTTP requests
	UserAgent = "WIT (Go net/http)"
	// APIVersion is the version of the Wit API supported
	APIVersion = "v=20140401"
)

// Client represents a client for the Wit API (https://wit.ai/docs/api)
type Client struct {
	APIBase string
}

// HTTPParams represents the HTTP parameters to pass along to the Wit API
type HTTPParams struct {
	Verb        string
	Resource    string
	ContentType string
	Data        []byte
}

// Stores the ApiKey for the Wit API
var APIKey string

// NewClient creates a new client for the Wit API
//
//		client := wit.NewClient("<ACCESS-TOKEN>")
func NewClient(apiKey string) *Client {
	client := &Client{APIBase: "https://api.wit.ai"}
	APIKey = apiKey
	return client
}

// Provides a common facility for doing a DELETE on a Wit resource
//
//		result, err := delete("https://api.wit.ai/entities", "favorite_city")
func delete(resource string, id string) ([]byte, error) {
	httpParams := &HTTPParams{
		Resource: resource + "/" + id,
		Verb:     "DELETE",
	}
	return processRequest(httpParams)
}

// Provides a common facility for doing a GET on a Wit resource
//
//		result, err := get("https://api.wit.ai/entities/favorite_city")
func get(resource string) ([]byte, error) {
	httpParams := &HTTPParams{
		Resource: resource,
		Verb:     "GET",
	}
	return processRequest(httpParams)
}

// Provides a common facility for doing a POST on a Wit resource. Takes
// JSON []byte for the data argument.
//
//		result, err := post("https://api.wit.ai/entities", entity)
func post(resource string, data []byte) ([]byte, error) {
	httpParams := &HTTPParams{"POST", resource, "application/json", data}
	return processRequest(httpParams)
}

// Provides a common facility for doing a POST with a file on a Wit resource.
//
//		result, err := postFile("https://api.wit.ai/messages", message)
func postFile(resource string, request *MessageRequest) ([]byte, error) {
	if request.File != "" {
		file, err := os.Open(request.File)
		if err != nil {
			return nil, err
		}
		defer file.Close()
		stats, statsErr := file.Stat()
		if statsErr != nil {
			return nil, statsErr
		}
		size := stats.Size()
		data := make([]byte, size)
		file.Read(data)
		httpParams := &HTTPParams{"POST", resource, request.ContentType, data}
		return processRequest(httpParams)
	}

	if request.FileContents != nil {
		httpParams := &HTTPParams{"POST", resource, request.ContentType, request.FileContents}
		return processRequest(httpParams)
		// } else {
		// return nil, errors.New("Must provide a filename or contents")
	}

	return nil, errors.New("Must provide a filename or contents")
}

// Provides a common facility for doing a PUT on a Wit resource.
//
//		result, err := put("https://api.wit.ai/entities", entity)
func put(resource string, data []byte) ([]byte, error) {
	httpParams := &HTTPParams{"PUT", resource, "application/json", data}
	return processRequest(httpParams)
}

// Processes an HTTP request to the Wit API
func processRequest(httpParams *HTTPParams) ([]byte, error) {
	regex := regexp.MustCompile(`\?`)
	if regex.MatchString(httpParams.Resource) {
		httpParams.Resource += "&" + APIVersion
	} else {
		httpParams.Resource += "?" + APIVersion
	}
	reader := bytes.NewReader(httpParams.Data)
	httpClient := &http.Client{}
	req, err := http.NewRequest(httpParams.Verb, httpParams.Resource, reader)
	if err != nil {
		return nil, err
	}
	setHeaders(req, httpParams.ContentType)
	result, err := httpClient.Do(req)
	if err != nil {
		return nil, err
	}
	body, err := ioutil.ReadAll(result.Body)
	result.Body.Close()
	if result.StatusCode != 200 {
		return nil, errors.New(http.StatusText(result.StatusCode))
	}
	return body, nil
}

// Sets the custom headers required for the Wit.ai API
//
//		setHeaders(req, httpParams.ContentType)
func setHeaders(req *http.Request, contentType string) {
	req.Header.Add("Authorization", "Bearer "+APIKey)
	req.Header.Set("Content-Type", contentType)
	req.Header.Set("Accept", "application/json")
}
<|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 model

import (
	"fmt"
	"strconv"

	"k8s.io/kops/pkg/apis/kops"
	"k8s.io/kops/upup/pkg/fi"
	"k8s.io/kops/upup/pkg/fi/cloudup/awstasks"

	"github.com/golang/glog"
)

type Protocol int

const (
	ProtocolIPIP Protocol = 4
)

// FirewallModelBuilder configures firewall network objects
type FirewallModelBuilder struct {
	*KopsModelContext
	Lifecycle *fi.Lifecycle
}

var _ fi.ModelBuilder = &FirewallModelBuilder{}

func (b *FirewallModelBuilder) Build(c *fi.ModelBuilderContext) error {
	if err := b.buildNodeRules(c); err != nil {
		return err
	}
	if err := b.buildMasterRules(c); err != nil {
		return err
	}
	return nil
}

func (b *FirewallModelBuilder) buildNodeRules(c *fi.ModelBuilderContext) error {
	name := "nodes." + b.ClusterName()

	{
		t := &awstasks.SecurityGroup{
			Name:             s(name),
			Lifecycle:        b.Lifecycle,
			VPC:              b.LinkToVPC(),
			Description:      s("Security group for nodes"),
			RemoveExtraRules: []string{"port=22"},
		}
		c.AddTask(t)
	}

	// Allow full egress
	{
		t := &awstasks.SecurityGroupRule{
			Name:          s("node-egress"),
			Lifecycle:     b.Lifecycle,
			SecurityGroup: b.LinkToSecurityGroup(kops.InstanceGroupRoleNode),
			Egress:        fi.Bool(true),
			CIDR:          s("0.0.0.0/0"),
		}
		c.AddTask(t)
	}

	// Nodes can talk to nodes
	{
		t := &awstasks.SecurityGroupRule{
			Name:          s("all-node-to-node"),
			Lifecycle:     b.Lifecycle,
			SecurityGroup: b.LinkToSecurityGroup(kops.InstanceGroupRoleNode),
			SourceGroup:   b.LinkToSecurityGroup(kops.InstanceGroupRoleNode),
		}
		c.AddTask(t)
	}

	// We _should_ block per port... but:
	// * It causes e2e tests to break
	// * Users expect to be able to reach pods
	// * If users are running an overlay, we punch a hole in it anyway
	//b.applyNodeToMasterAllowSpecificPorts(c)
	b.applyNodeToMasterBlockSpecificPorts(c)

	return nil
}

func (b *FirewallModelBuilder) applyNodeToMasterAllowSpecificPorts(c *fi.ModelBuilderContext) {
	// TODO: We need to remove the ALL rule
	//W1229 12:32:22.300132    9003 executor.go:109] error running task "SecurityGroupRule/node-to-master-443" (9m58s remaining to succeed): error creating SecurityGroupIngress: InvalidPermission.Duplicate: the specified rule "peer: sg-f6b1a68b, ALL, ALLOW" already exists
	//status code: 400, request id: 6a69627f-9a26-4bd0-b294-a9a96f89bc46

	udpPorts := []int64{}
	tcpPorts := []int64{}
	protocols := []Protocol{}

	// allow access to API
	tcpPorts = append(tcpPorts, 443)

	// allow cadvisor
	tcpPorts = append(tcpPorts, 4194)

	// kubelet read-only used by heapster
	tcpPorts = append(tcpPorts, 10255)

	if b.Cluster.Spec.Networking != nil {
		if b.Cluster.Spec.Networking.Kopeio != nil {
			// VXLAN over UDP
			udpPorts = append(udpPorts, 4789)
		}

		if b.Cluster.Spec.Networking.Weave != nil {
			udpPorts = append(udpPorts, 6783)
			tcpPorts = append(tcpPorts, 6783)
			udpPorts = append(udpPorts, 6784)
		}

		if b.Cluster.Spec.Networking.Flannel != nil {
			switch b.Cluster.Spec.Networking.Flannel.Backend {
			case "", "udp":
				udpPorts = append(udpPorts, 8285)
			case "vxlan":
				udpPorts = append(udpPorts, 8472)
			default:
				glog.Warningf("unknown flannel networking backend %q", b.Cluster.Spec.Networking.Flannel.Backend)
			}
		}

		if b.Cluster.Spec.Networking.Calico != nil {
			// Calico needs to access etcd
			// TODO: Remove, replace with etcd in calico manifest
			// https://coreos.com/etcd/docs/latest/v2/configuration.html
			glog.Warningf("Opening etcd port on masters for access from the nodes, for calico.  This is unsafe in untrusted environments.")
			tcpPorts = append(tcpPorts, 4001)
			tcpPorts = append(tcpPorts, 179)
			protocols = append(protocols, ProtocolIPIP)
		}

		if b.Cluster.Spec.Networking.Romana != nil {
			// Romana needs to access etcd
			glog.Warningf("Opening etcd port on masters for access from the nodes, for romana.  This is unsafe in untrusted environments.")
			tcpPorts = append(tcpPorts, 4001)
			tcpPorts = append(tcpPorts, 9600)
		}
	}

	for _, udpPort := range udpPorts {
		c.AddTask(&awstasks.SecurityGroupRule{
			Name:          s(fmt.Sprintf("node-to-master-udp-%d", udpPort)),
			Lifecycle:     b.Lifecycle,
			SecurityGroup: b.LinkToSecurityGroup(kops.InstanceGroupRoleMaster),
			SourceGroup:   b.LinkToSecurityGroup(kops.InstanceGroupRoleNode),
			FromPort:      i64(udpPort),
			ToPort:        i64(udpPort),
			Protocol:      s("udp"),
		})
	}
	for _, tcpPort := range tcpPorts {
		c.AddTask(&awstasks.SecurityGroupRule{
			Name:          s(fmt.Sprintf("node-to-master-tcp-%d", tcpPort)),
			Lifecycle:     b.Lifecycle,
			SecurityGroup: b.LinkToSecurityGroup(kops.InstanceGroupRoleMaster),
			SourceGroup:   b.LinkToSecurityGroup(kops.InstanceGroupRoleNode),
			FromPort:      i64(tcpPort),
			ToPort:        i64(tcpPort),
			Protocol:      s("tcp"),
		})
	}
	for _, protocol := range protocols {
		awsName := strconv.Itoa(int(protocol))
		name := awsName
		switch protocol {
		case ProtocolIPIP:
			name = "ipip"
		default:
			glog.Warningf("unknown protocol %q - naming by number", awsName)
		}

		c.AddTask(&awstasks.SecurityGroupRule{
			Name:          s("node-to-master-protocol-" + name),
			Lifecycle:     b.Lifecycle,
			SecurityGroup: b.LinkToSecurityGroup(kops.InstanceGroupRoleMaster),
			SourceGroup:   b.LinkToSecurityGroup(kops.InstanceGroupRoleNode),
			Protocol:      s(awsName),
		})
	}
}

func (b *FirewallModelBuilder) applyNodeToMasterBlockSpecificPorts(c *fi.ModelBuilderContext) {
	type portRange struct {
		From int
		To   int
	}

	// TODO: Make less hacky
	// TODO: Fix management - we need a wildcard matcher now
	tcpRanges := []portRange{{From: 1, To: 4000}, {From: 4003, To: 65535}}
	udpRanges := []portRange{{From: 1, To: 65535}}
	protocols := []Protocol{}

	if b.Cluster.Spec.Networking.Calico != nil {
		// Calico needs to access etcd
		// TODO: Remove, replace with etcd in calico manifest
		glog.Warningf("Opening etcd port on masters for access from the nodes, for calico.  This is unsafe in untrusted environments.")
		tcpRanges = []portRange{{From: 1, To: 4001}, {From: 4003, To: 65535}}
		protocols = append(protocols, ProtocolIPIP)
	}

	if b.Cluster.Spec.Networking.Romana != nil {
		// Romana needs to access etcd
		glog.Warningf("Opening etcd port on masters for access from the nodes, for romana.  This is unsafe in untrusted environments.")
		tcpRanges = []portRange{{From: 1, To: 4001}, {From: 4003, To: 65535}}
		protocols = append(protocols, ProtocolIPIP)
	}

	for _, r := range udpRanges {
		c.AddTask(&awstasks.SecurityGroupRule{
			Name:          s(fmt.Sprintf("node-to-master-udp-%d-%d", r.From, r.To)),
			Lifecycle:     b.Lifecycle,
			SecurityGroup: b.LinkToSecurityGroup(kops.InstanceGroupRoleMaster),
			SourceGroup:   b.LinkToSecurityGroup(kops.InstanceGroupRoleNode),
			FromPort:      i64(int64(r.From)),
			ToPort:        i64(int64(r.To)),
			Protocol:      s("udp"),
		})
	}
	for _, r := range tcpRanges {
		c.AddTask(&awstasks.SecurityGroupRule{
			Name:          s(fmt.Sprintf("node-to-master-tcp-%d-%d", r.From, r.To)),
			Lifecycle:     b.Lifecycle,
			SecurityGroup: b.LinkToSecurityGroup(kops.InstanceGroupRoleMaster),
			SourceGroup:   b.LinkToSecurityGroup(kops.InstanceGroupRoleNode),
			FromPort:      i64(int64(r.From)),
			ToPort:        i64(int64(r.To)),
			Protocol:      s("tcp"),
		})
	}
	for _, protocol := range protocols {
		awsName := strconv.Itoa(int(protocol))
		name := awsName
		switch protocol {
		case ProtocolIPIP:
			name = "ipip"
		default:
			glog.Warningf("unknown protocol %q - naming by number", awsName)
		}

		c.AddTask(&awstasks.SecurityGroupRule{
			Name:          s("node-to-master-protocol-" + name),
			Lifecycle:     b.Lifecycle,
			SecurityGroup: b.LinkToSecurityGroup(kops.InstanceGroupRoleMaster),
			SourceGroup:   b.LinkToSecurityGroup(kops.InstanceGroupRoleNode),
			Protocol:      s(awsName),
		})
	}
}

func (b *FirewallModelBuilder) buildMasterRules(c *fi.ModelBuilderContext) error {
	name := "masters." + b.ClusterName()

	{
		t := &awstasks.SecurityGroup{
			Name:        s(name),
			Lifecycle:   b.Lifecycle,
			VPC:         b.LinkToVPC(),
			Description: s("Security group for masters"),
			RemoveExtraRules: []string{
				"port=22",   // SSH
				"port=443",  // k8s api
				"port=4001", // etcd main (etcd events is 4002)
				"port=4789", // VXLAN
				"port=179",  // Calico

				// TODO: UDP vs TCP
				// TODO: Protocol 4 for calico
			},
		}
		c.AddTask(t)
	}

	// Allow full egress
	{
		t := &awstasks.SecurityGroupRule{
			Name:          s("master-egress"),
			Lifecycle:     b.Lifecycle,
			SecurityGroup: b.LinkToSecurityGroup(kops.InstanceGroupRoleMaster),
			Egress:        fi.Bool(true),
			CIDR:          s("0.0.0.0/0"),
		}
		c.AddTask(t)
	}

	// Masters can talk to masters
	{
		t := &awstasks.SecurityGroupRule{
			Name:          s("all-master-to-master"),
			Lifecycle:     b.Lifecycle,
			SecurityGroup: b.LinkToSecurityGroup(kops.InstanceGroupRoleMaster),
			SourceGroup:   b.LinkToSecurityGroup(kops.InstanceGroupRoleMaster),
		}
		c.AddTask(t)
	}

	// Masters can talk to nodes
	{
		t := &awstasks.SecurityGroupRule{
			Name:          s("all-master-to-node"),
			Lifecycle:     b.Lifecycle,
			SecurityGroup: b.LinkToSecurityGroup(kops.InstanceGroupRoleNode),
			SourceGroup:   b.LinkToSecurityGroup(kops.InstanceGroupRoleMaster),
		}
		c.AddTask(t)
	}

	return nil
}
<commit_msg>Add node-to-master IPIP to kuberouter<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 model

import (
	"fmt"
	"strconv"

	"k8s.io/kops/pkg/apis/kops"
	"k8s.io/kops/upup/pkg/fi"
	"k8s.io/kops/upup/pkg/fi/cloudup/awstasks"

	"github.com/golang/glog"
)

type Protocol int

const (
	ProtocolIPIP Protocol = 4
)

// FirewallModelBuilder configures firewall network objects
type FirewallModelBuilder struct {
	*KopsModelContext
	Lifecycle *fi.Lifecycle
}

var _ fi.ModelBuilder = &FirewallModelBuilder{}

func (b *FirewallModelBuilder) Build(c *fi.ModelBuilderContext) error {
	if err := b.buildNodeRules(c); err != nil {
		return err
	}
	if err := b.buildMasterRules(c); err != nil {
		return err
	}
	return nil
}

func (b *FirewallModelBuilder) buildNodeRules(c *fi.ModelBuilderContext) error {
	name := "nodes." + b.ClusterName()

	{
		t := &awstasks.SecurityGroup{
			Name:             s(name),
			Lifecycle:        b.Lifecycle,
			VPC:              b.LinkToVPC(),
			Description:      s("Security group for nodes"),
			RemoveExtraRules: []string{"port=22"},
		}
		c.AddTask(t)
	}

	// Allow full egress
	{
		t := &awstasks.SecurityGroupRule{
			Name:          s("node-egress"),
			Lifecycle:     b.Lifecycle,
			SecurityGroup: b.LinkToSecurityGroup(kops.InstanceGroupRoleNode),
			Egress:        fi.Bool(true),
			CIDR:          s("0.0.0.0/0"),
		}
		c.AddTask(t)
	}

	// Nodes can talk to nodes
	{
		t := &awstasks.SecurityGroupRule{
			Name:          s("all-node-to-node"),
			Lifecycle:     b.Lifecycle,
			SecurityGroup: b.LinkToSecurityGroup(kops.InstanceGroupRoleNode),
			SourceGroup:   b.LinkToSecurityGroup(kops.InstanceGroupRoleNode),
		}
		c.AddTask(t)
	}

	// We _should_ block per port... but:
	// * It causes e2e tests to break
	// * Users expect to be able to reach pods
	// * If users are running an overlay, we punch a hole in it anyway
	//b.applyNodeToMasterAllowSpecificPorts(c)
	b.applyNodeToMasterBlockSpecificPorts(c)

	return nil
}

func (b *FirewallModelBuilder) applyNodeToMasterAllowSpecificPorts(c *fi.ModelBuilderContext) {
	// TODO: We need to remove the ALL rule
	//W1229 12:32:22.300132    9003 executor.go:109] error running task "SecurityGroupRule/node-to-master-443" (9m58s remaining to succeed): error creating SecurityGroupIngress: InvalidPermission.Duplicate: the specified rule "peer: sg-f6b1a68b, ALL, ALLOW" already exists
	//status code: 400, request id: 6a69627f-9a26-4bd0-b294-a9a96f89bc46

	udpPorts := []int64{}
	tcpPorts := []int64{}
	protocols := []Protocol{}

	// allow access to API
	tcpPorts = append(tcpPorts, 443)

	// allow cadvisor
	tcpPorts = append(tcpPorts, 4194)

	// kubelet read-only used by heapster
	tcpPorts = append(tcpPorts, 10255)

	if b.Cluster.Spec.Networking != nil {
		if b.Cluster.Spec.Networking.Kopeio != nil {
			// VXLAN over UDP
			udpPorts = append(udpPorts, 4789)
		}

		if b.Cluster.Spec.Networking.Weave != nil {
			udpPorts = append(udpPorts, 6783)
			tcpPorts = append(tcpPorts, 6783)
			udpPorts = append(udpPorts, 6784)
		}

		if b.Cluster.Spec.Networking.Flannel != nil {
			switch b.Cluster.Spec.Networking.Flannel.Backend {
			case "", "udp":
				udpPorts = append(udpPorts, 8285)
			case "vxlan":
				udpPorts = append(udpPorts, 8472)
			default:
				glog.Warningf("unknown flannel networking backend %q", b.Cluster.Spec.Networking.Flannel.Backend)
			}
		}

		if b.Cluster.Spec.Networking.Calico != nil {
			// Calico needs to access etcd
			// TODO: Remove, replace with etcd in calico manifest
			// https://coreos.com/etcd/docs/latest/v2/configuration.html
			glog.Warningf("Opening etcd port on masters for access from the nodes, for calico.  This is unsafe in untrusted environments.")
			tcpPorts = append(tcpPorts, 4001)
			tcpPorts = append(tcpPorts, 179)
			protocols = append(protocols, ProtocolIPIP)
		}

		if b.Cluster.Spec.Networking.Romana != nil {
			// Romana needs to access etcd
			glog.Warningf("Opening etcd port on masters for access from the nodes, for romana.  This is unsafe in untrusted environments.")
			tcpPorts = append(tcpPorts, 4001)
			tcpPorts = append(tcpPorts, 9600)
		}

		if b.Cluster.Spec.Networking.Kuberouter != nil {
			protocols = append(protocols, ProtocolIPIP)
		}
	}

	for _, udpPort := range udpPorts {
		c.AddTask(&awstasks.SecurityGroupRule{
			Name:          s(fmt.Sprintf("node-to-master-udp-%d", udpPort)),
			Lifecycle:     b.Lifecycle,
			SecurityGroup: b.LinkToSecurityGroup(kops.InstanceGroupRoleMaster),
			SourceGroup:   b.LinkToSecurityGroup(kops.InstanceGroupRoleNode),
			FromPort:      i64(udpPort),
			ToPort:        i64(udpPort),
			Protocol:      s("udp"),
		})
	}
	for _, tcpPort := range tcpPorts {
		c.AddTask(&awstasks.SecurityGroupRule{
			Name:          s(fmt.Sprintf("node-to-master-tcp-%d", tcpPort)),
			Lifecycle:     b.Lifecycle,
			SecurityGroup: b.LinkToSecurityGroup(kops.InstanceGroupRoleMaster),
			SourceGroup:   b.LinkToSecurityGroup(kops.InstanceGroupRoleNode),
			FromPort:      i64(tcpPort),
			ToPort:        i64(tcpPort),
			Protocol:      s("tcp"),
		})
	}
	for _, protocol := range protocols {
		awsName := strconv.Itoa(int(protocol))
		name := awsName
		switch protocol {
		case ProtocolIPIP:
			name = "ipip"
		default:
			glog.Warningf("unknown protocol %q - naming by number", awsName)
		}

		c.AddTask(&awstasks.SecurityGroupRule{
			Name:          s("node-to-master-protocol-" + name),
			Lifecycle:     b.Lifecycle,
			SecurityGroup: b.LinkToSecurityGroup(kops.InstanceGroupRoleMaster),
			SourceGroup:   b.LinkToSecurityGroup(kops.InstanceGroupRoleNode),
			Protocol:      s(awsName),
		})
	}
}

func (b *FirewallModelBuilder) applyNodeToMasterBlockSpecificPorts(c *fi.ModelBuilderContext) {
	type portRange struct {
		From int
		To   int
	}

	// TODO: Make less hacky
	// TODO: Fix management - we need a wildcard matcher now
	tcpRanges := []portRange{{From: 1, To: 4000}, {From: 4003, To: 65535}}
	udpRanges := []portRange{{From: 1, To: 65535}}
	protocols := []Protocol{}

	if b.Cluster.Spec.Networking.Calico != nil {
		// Calico needs to access etcd
		// TODO: Remove, replace with etcd in calico manifest
		glog.Warningf("Opening etcd port on masters for access from the nodes, for calico.  This is unsafe in untrusted environments.")
		tcpRanges = []portRange{{From: 1, To: 4001}, {From: 4003, To: 65535}}
		protocols = append(protocols, ProtocolIPIP)
	}

	if b.Cluster.Spec.Networking.Romana != nil {
		// Romana needs to access etcd
		glog.Warningf("Opening etcd port on masters for access from the nodes, for romana.  This is unsafe in untrusted environments.")
		tcpRanges = []portRange{{From: 1, To: 4001}, {From: 4003, To: 65535}}
		protocols = append(protocols, ProtocolIPIP)
	}

	if b.Cluster.Spec.Networking.Kuberouter != nil {
		protocols = append(protocols, ProtocolIPIP)
	}

	for _, r := range udpRanges {
		c.AddTask(&awstasks.SecurityGroupRule{
			Name:          s(fmt.Sprintf("node-to-master-udp-%d-%d", r.From, r.To)),
			Lifecycle:     b.Lifecycle,
			SecurityGroup: b.LinkToSecurityGroup(kops.InstanceGroupRoleMaster),
			SourceGroup:   b.LinkToSecurityGroup(kops.InstanceGroupRoleNode),
			FromPort:      i64(int64(r.From)),
			ToPort:        i64(int64(r.To)),
			Protocol:      s("udp"),
		})
	}
	for _, r := range tcpRanges {
		c.AddTask(&awstasks.SecurityGroupRule{
			Name:          s(fmt.Sprintf("node-to-master-tcp-%d-%d", r.From, r.To)),
			Lifecycle:     b.Lifecycle,
			SecurityGroup: b.LinkToSecurityGroup(kops.InstanceGroupRoleMaster),
			SourceGroup:   b.LinkToSecurityGroup(kops.InstanceGroupRoleNode),
			FromPort:      i64(int64(r.From)),
			ToPort:        i64(int64(r.To)),
			Protocol:      s("tcp"),
		})
	}
	for _, protocol := range protocols {
		awsName := strconv.Itoa(int(protocol))
		name := awsName
		switch protocol {
		case ProtocolIPIP:
			name = "ipip"
		default:
			glog.Warningf("unknown protocol %q - naming by number", awsName)
		}

		c.AddTask(&awstasks.SecurityGroupRule{
			Name:          s("node-to-master-protocol-" + name),
			Lifecycle:     b.Lifecycle,
			SecurityGroup: b.LinkToSecurityGroup(kops.InstanceGroupRoleMaster),
			SourceGroup:   b.LinkToSecurityGroup(kops.InstanceGroupRoleNode),
			Protocol:      s(awsName),
		})
	}
}

func (b *FirewallModelBuilder) buildMasterRules(c *fi.ModelBuilderContext) error {
	name := "masters." + b.ClusterName()

	{
		t := &awstasks.SecurityGroup{
			Name:        s(name),
			Lifecycle:   b.Lifecycle,
			VPC:         b.LinkToVPC(),
			Description: s("Security group for masters"),
			RemoveExtraRules: []string{
				"port=22",   // SSH
				"port=443",  // k8s api
				"port=4001", // etcd main (etcd events is 4002)
				"port=4789", // VXLAN
				"port=179",  // Calico

				// TODO: UDP vs TCP
				// TODO: Protocol 4 for calico
			},
		}
		c.AddTask(t)
	}

	// Allow full egress
	{
		t := &awstasks.SecurityGroupRule{
			Name:          s("master-egress"),
			Lifecycle:     b.Lifecycle,
			SecurityGroup: b.LinkToSecurityGroup(kops.InstanceGroupRoleMaster),
			Egress:        fi.Bool(true),
			CIDR:          s("0.0.0.0/0"),
		}
		c.AddTask(t)
	}

	// Masters can talk to masters
	{
		t := &awstasks.SecurityGroupRule{
			Name:          s("all-master-to-master"),
			Lifecycle:     b.Lifecycle,
			SecurityGroup: b.LinkToSecurityGroup(kops.InstanceGroupRoleMaster),
			SourceGroup:   b.LinkToSecurityGroup(kops.InstanceGroupRoleMaster),
		}
		c.AddTask(t)
	}

	// Masters can talk to nodes
	{
		t := &awstasks.SecurityGroupRule{
			Name:          s("all-master-to-node"),
			Lifecycle:     b.Lifecycle,
			SecurityGroup: b.LinkToSecurityGroup(kops.InstanceGroupRoleNode),
			SourceGroup:   b.LinkToSecurityGroup(kops.InstanceGroupRoleMaster),
		}
		c.AddTask(t)
	}

	return nil
}
<|endoftext|>


<commit_before>package gode

import (
	"os"
	"path/filepath"
	"runtime"
)

// The Node version to install.
// Override this by setting client.Version.
const DefaultNodeVersion = "v0.10.32"

// Client is the interface between Node and Go.
// It also setups up the Node environment if needed.
type Client struct {
	RootPath    string
	NodePath    string
	NpmPath     string
	ModulesPath string
	Version     string
	NodeURL     string
}

// NewClient creates a new Client at the specified rootPath
// The Node installation can then be setup here with client.Setup()
func NewClient(rootPath string) *Client {
	client := &Client{
		RootPath:    rootPath,
		NodePath:    filepath.Join(rootPath, nodeBase(DefaultNodeVersion), "bin", "node"),
		NpmPath:     filepath.Join(rootPath, nodeBase(DefaultNodeVersion), "bin", "npm"),
		ModulesPath: filepath.Join(rootPath, "lib", "node_modules"),
		Version:     DefaultNodeVersion,
		NodeURL:     nodeURL(DefaultNodeVersion),
	}
	os.Setenv("NODE_PATH", client.ModulesPath)
	os.Setenv("NPM_CONFIG_GLOBAL", "true")
	os.Setenv("NPM_CONFIG_PREFIX", client.RootPath)
	os.Setenv("NPM_CONFIG_SPINNER", "false")

	return client
}

func nodeBase(version string) string {
	switch {
	case runtime.GOARCH == "386":
		return "node-" + version + "-" + runtime.GOOS + "-x86"
	default:
		return "node-" + version + "-" + runtime.GOOS + "-x64"
	}
}

func nodeURL(version string) string {
	switch {
	case runtime.GOOS == "windows" && runtime.GOARCH == "386":
		return "http://nodejs.org/dist/" + version + "/node.exe"
	case runtime.GOOS == "windows" && runtime.GOARCH == "amd64":
		return "http://nodejs.org/dist/" + version + "/x64/node.exe"
	case runtime.GOARCH == "386":
		return "http://nodejs.org/dist/" + version + "/" + nodeBase(version) + ".tar.gz"
	default:
		return "http://nodejs.org/dist/" + version + "/" + nodeBase(version) + ".tar.gz"
	}
}
<commit_msg>fixed config var<commit_after>package gode

import (
	"os"
	"path/filepath"
	"runtime"
)

// The Node version to install.
// Override this by setting client.Version.
const DefaultNodeVersion = "v0.10.32"

// Client is the interface between Node and Go.
// It also setups up the Node environment if needed.
type Client struct {
	RootPath    string
	NodePath    string
	NpmPath     string
	ModulesPath string
	Version     string
	NodeURL     string
}

// NewClient creates a new Client at the specified rootPath
// The Node installation can then be setup here with client.Setup()
func NewClient(rootPath string) *Client {
	client := &Client{
		RootPath:    rootPath,
		NodePath:    filepath.Join(rootPath, nodeBase(DefaultNodeVersion), "bin", "node"),
		NpmPath:     filepath.Join(rootPath, nodeBase(DefaultNodeVersion), "bin", "npm"),
		ModulesPath: filepath.Join(rootPath, "lib", "node_modules"),
		Version:     DefaultNodeVersion,
		NodeURL:     nodeURL(DefaultNodeVersion),
	}
	os.Setenv("NODE_PATH", client.ModulesPath)
	os.Setenv("NPM_CONFIG_GLOBAL", "true")
	os.Setenv("NPM_CONFIG_PREFIX", client.RootPath)
	os.Setenv("NPM_CONFIG_SPIN", "false")

	return client
}

func nodeBase(version string) string {
	switch {
	case runtime.GOARCH == "386":
		return "node-" + version + "-" + runtime.GOOS + "-x86"
	default:
		return "node-" + version + "-" + runtime.GOOS + "-x64"
	}
}

func nodeURL(version string) string {
	switch {
	case runtime.GOOS == "windows" && runtime.GOARCH == "386":
		return "http://nodejs.org/dist/" + version + "/node.exe"
	case runtime.GOOS == "windows" && runtime.GOARCH == "amd64":
		return "http://nodejs.org/dist/" + version + "/x64/node.exe"
	case runtime.GOARCH == "386":
		return "http://nodejs.org/dist/" + version + "/" + nodeBase(version) + ".tar.gz"
	default:
		return "http://nodejs.org/dist/" + version + "/" + nodeBase(version) + ".tar.gz"
	}
}
<|endoftext|>