<commit_before>// Copyright 2013 Matthew Baird
// 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 elastigo

import (
	"encoding/json"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"net/url"
	"reflect"
	"sort"
	"strings"
	"testing"
)

var (
	mux    *http.ServeMux
	server *httptest.Server
)

func setup(t *testing.T) *Conn {
	mux = http.NewServeMux()
	server = httptest.NewServer(mux)
	c := NewTestConn()

	serverURL, err := url.Parse(server.URL)
	if err != nil {
		t.Fatalf("Error: %v", err)
	}

	c.Domain = strings.Split(serverURL.Host, ":")[0]
	c.Port = strings.Split(serverURL.Host, ":")[1]

	return c
}

func teardown() {
	server.Close()
}

type TestStruct struct {
	Id            string `json:"id" elastic:"index:not_analyzed"`
	DontIndex     string `json:"dontIndex" elastic:"index:no"`
	Number        int    `json:"number" elastic:"type:integer,index:analyzed"`
	Omitted       string `json:"-"`
	NoJson        string `elastic:"type:string"`
	unexported    string
	JsonOmitEmpty string `json:"jsonOmitEmpty,omitempty" elastic:"type:string"`
	Embedded
	Inner        InnerStruct   `json:"inner"`
	InnerP       *InnerStruct  `json:"pointer_to_inner"`
	InnerS       []InnerStruct `json:"slice_of_inner"`
	MultiAnalyze string        `json:"multi_analyze"`
	NestedObject NestedStruct  `json:"nestedObject" elastic:"type:nested"`
}

type Embedded struct {
	EmbeddedField string `json:"embeddedField" elastic:"type:string"`
}

type InnerStruct struct {
	InnerField string `json:"innerField" elastic:"type:date"`
}

type NestedStruct struct {
	InnerField string `json:"innerField" elastic:"type:date"`
}

// Sorting string
// RuneSlice implements sort.Interface (http://golang.org/pkg/sort/#Interface)
type RuneSlice []rune

func (p RuneSlice) Len() int           { return len(p) }
func (p RuneSlice) Less(i, j int) bool { return p[i] < p[j] }
func (p RuneSlice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }

// sorted func returns string with sorted characters
func sorted(s string) string {
	runes := []rune(s)
	sort.Sort(RuneSlice(runes))
	return string(runes)
}

func TestPutMapping(t *testing.T) {
	c := setup(t)
	defer teardown()

	options := MappingOptions{
		Timestamp: TimestampOptions{Enabled: true},
		Id:        IdOptions{Index: "analyzed", Path: "id"},
		Parent:    &ParentOptions{Type: "testParent"},
		TTL:       &TTLOptions{Enabled: true, Default: "1w"},
		Properties: map[string]interface{}{
			// special properties that can't be expressed as tags
			"multi_analyze": map[string]interface{}{
				"type": "multi_field",
				"fields": map[string]map[string]string{
					"ma_analyzed":    {"type": "string", "index": "analyzed"},
					"ma_notanalyzed": {"type": "string", "index": "not_analyzed"},
				},
			},
		},
		DynamicTemplates: []map[string]interface{}{
			map[string]interface{}{
				"strings": map[string]interface{}{
					"match_mapping_type": "string",
					"mapping": map[string]interface{}{
						"type":  "string",
						"index": "not_analyzed",
					},
				},
			},
		},
	}
	expValue := MappingForType("myType", MappingOptions{
		Timestamp: TimestampOptions{Enabled: true},
		Id:        IdOptions{Index: "analyzed", Path: "id"},
		Parent:    &ParentOptions{Type: "testParent"},
		TTL:       &TTLOptions{Enabled: true, Default: "1w"},
		Properties: map[string]interface{}{
			"NoJson":        map[string]string{"type": "string"},
			"dontIndex":     map[string]string{"index": "no"},
			"embeddedField": map[string]string{"type": "string"},
			"id":            map[string]string{"index": "not_analyzed"},
			"jsonOmitEmpty": map[string]string{"type": "string"},
			"number":        map[string]string{"index": "analyzed", "type": "integer"},
			"multi_analyze": map[string]interface{}{
				"type": "multi_field",
				"fields": map[string]map[string]string{
					"ma_analyzed":    {"type": "string", "index": "analyzed"},
					"ma_notanalyzed": {"type": "string", "index": "not_analyzed"},
				},
			},
			"inner": map[string]map[string]map[string]string{
				"properties": {
					"innerField": {"type": "date"},
				},
			},
			"pointer_to_inner": map[string]map[string]map[string]string{
				"properties": {
					"innerField": {"type": "date"},
				},
			},
			"slice_of_inner": map[string]map[string]map[string]string{
				"properties": {
					"innerField": {"type": "date"},
				},
			},
			"nestedObject": map[string]interface{}{
				"type": "nested",
				"properties": map[string]map[string]string{
					"innerField": {"type": "date"},
				},
			},
		},
	})

	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		var value map[string]interface{}
		bd, err := ioutil.ReadAll(r.Body)
		json.NewDecoder(strings.NewReader(string(bd))).Decode(&value)
		expValJson, err := json.MarshalIndent(expValue, "", "  ")
		if err != nil {
			t.Errorf("Got error: %v", err)
		}
		valJson, err := json.MarshalIndent(value, "", "  ")
		if err != nil {
			t.Errorf("Got error: %v", err)
		}

		if sorted(string(expValJson)) != sorted(string(valJson)) {
			t.Errorf("Expected %s but got %s", string(expValJson), string(valJson))
		}
	})

	err := c.PutMapping("myIndex", "myType", TestStruct{}, options)
	if err != nil {
		t.Errorf("Error: %v", err)
	}
}

func TestPutMappingFromJSON(t *testing.T) {
	c := setup(t)
	defer teardown()
	/*
		options := MappingOptions{
			Timestamp: TimestampOptions{Enabled: true},
			Id:        IdOptions{Index: "analyzed", Path: "id"},
			Parent:    &ParentOptions{Type: "testParent"},
			Properties: map[string]interface{}{
				// special properties that can't be expressed as tags
				"multi_analyze": map[string]interface{}{
					"type": "multi_field",
					"fields": map[string]map[string]string{
						"ma_analyzed":    {"type": "string", "index": "analyzed"},
						"ma_notanalyzed": {"type": "string", "index": "not_analyzed"},
					},
				},
			},
			DynamicTemplates: []map[string]interface{}{
				"strings": map[string]interface{}{
					"match_mapping_type": "string",
					"mapping": {
						"type":  "string",
						"index": "not_analyzed",
					},
				},
			},
		}
	*/

	options := `{
					"myType": {
						"_id": {
							"index": "analyzed",
							"path": "id"
						},
						"_timestamp": {
							"enabled": true
						},
						"_parent": {
							"type": "testParent"
						},
						"properties": {
							"analyzed_string": {
								"type": "string",
								"index": "analyzed"
							},
							"multi_analyze": {
								"type": "multi_field",
								"fields": {
									"ma_analyzed":    {
										"type": "string",
										"index": "analyzed"
									},
									"ma_notanalyzed": {
										"type": "string",
										"index": "not_analyzed"
									}
								}
							}
						},
						"dynamic_templates": [
							{
								"strings": {
									"match_mapping_type": "string",
									"mapping": {
										"type": "string",
										"index": "not_analyzed"
									}
								}
							}
						]
					}
				}`

	expValue := map[string]interface{}{
		"myType": map[string]interface{}{
			"_timestamp": map[string]interface{}{
				"enabled": true,
			},
			"_id": map[string]interface{}{
				"index": "analyzed",
				"path":  "id",
			},
			"_parent": map[string]interface{}{
				"type": "testParent",
			},
			"properties": map[string]interface{}{
				"analyzed_string": map[string]string{
					"type":  "string",
					"index": "analyzed",
				},
				"multi_analyze": map[string]interface{}{
					"type": "multi_field",
					"fields": map[string]map[string]string{
						"ma_analyzed":    {"type": "string", "index": "analyzed"},
						"ma_notanalyzed": {"type": "string", "index": "not_analyzed"},
					},
				},
			},
		},
	}

	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		var value map[string]interface{}
		bd, err := ioutil.ReadAll(r.Body)
		err = json.Unmarshal(bd, &value)
		if err != nil {
			t.Errorf("Got error: %v", err)
		}
		expValJson, err := json.MarshalIndent(expValue, "", "  ")
		if err != nil {
			t.Errorf("Got error: %v", err)
		}

		valJson, err := json.MarshalIndent(value, "", "  ")
		if err != nil {
			t.Errorf("Got error: %v", err)
		}

		if sorted(string(expValJson)) != sorted(string(valJson)) {
			t.Errorf("Expected %s but got %s", string(expValJson), string(valJson))
		}
	})

	err := c.PutMappingFromJSON("myIndex", "myType", []byte(options))
	if err != nil {
		t.Errorf("Error: %v", err)
	}
}

type StructWithEmptyElasticTag struct {
	Field string `json:"field" elastic:""`
}

func TestPutMapping_empty_elastic_tag_is_accepted(t *testing.T) {
	properties := map[string]interface{}{}
	getProperties(reflect.TypeOf(StructWithEmptyElasticTag{}), properties)
	if len(properties) != 0 {
		t.Errorf("Expected empty properites but got: %v", properties)
	}
}
<commit_msg>Fix tests for DynamicMappings<commit_after>// Copyright 2013 Matthew Baird
// 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 elastigo

import (
	"encoding/json"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"net/url"
	"reflect"
	"sort"
	"strings"
	"testing"
)

var (
	mux    *http.ServeMux
	server *httptest.Server
)

func setup(t *testing.T) *Conn {
	mux = http.NewServeMux()
	server = httptest.NewServer(mux)
	c := NewTestConn()

	serverURL, err := url.Parse(server.URL)
	if err != nil {
		t.Fatalf("Error: %v", err)
	}

	c.Domain = strings.Split(serverURL.Host, ":")[0]
	c.Port = strings.Split(serverURL.Host, ":")[1]

	return c
}

func teardown() {
	server.Close()
}

type TestStruct struct {
	Id            string `json:"id" elastic:"index:not_analyzed"`
	DontIndex     string `json:"dontIndex" elastic:"index:no"`
	Number        int    `json:"number" elastic:"type:integer,index:analyzed"`
	Omitted       string `json:"-"`
	NoJson        string `elastic:"type:string"`
	unexported    string
	JsonOmitEmpty string `json:"jsonOmitEmpty,omitempty" elastic:"type:string"`
	Embedded
	Inner        InnerStruct   `json:"inner"`
	InnerP       *InnerStruct  `json:"pointer_to_inner"`
	InnerS       []InnerStruct `json:"slice_of_inner"`
	MultiAnalyze string        `json:"multi_analyze"`
	NestedObject NestedStruct  `json:"nestedObject" elastic:"type:nested"`
}

type Embedded struct {
	EmbeddedField string `json:"embeddedField" elastic:"type:string"`
}

type InnerStruct struct {
	InnerField string `json:"innerField" elastic:"type:date"`
}

type NestedStruct struct {
	InnerField string `json:"innerField" elastic:"type:date"`
}

// Sorting string
// RuneSlice implements sort.Interface (http://golang.org/pkg/sort/#Interface)
type RuneSlice []rune

func (p RuneSlice) Len() int           { return len(p) }
func (p RuneSlice) Less(i, j int) bool { return p[i] < p[j] }
func (p RuneSlice) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }

// sorted func returns string with sorted characters
func sorted(s string) string {
	runes := []rune(s)
	sort.Sort(RuneSlice(runes))
	return string(runes)
}

func TestPutMapping(t *testing.T) {
	c := setup(t)
	defer teardown()

	options := MappingOptions{
		Timestamp: TimestampOptions{Enabled: true},
		Id:        IdOptions{Index: "analyzed", Path: "id"},
		Parent:    &ParentOptions{Type: "testParent"},
		TTL:       &TTLOptions{Enabled: true, Default: "1w"},
		Properties: map[string]interface{}{
			// special properties that can't be expressed as tags
			"multi_analyze": map[string]interface{}{
				"type": "multi_field",
				"fields": map[string]map[string]string{
					"ma_analyzed":    {"type": "string", "index": "analyzed"},
					"ma_notanalyzed": {"type": "string", "index": "not_analyzed"},
				},
			},
		},
		DynamicTemplates: []map[string]interface{}{
			map[string]interface{}{
				"strings": map[string]interface{}{
					"match_mapping_type": "string",
					"mapping": map[string]interface{}{
						"type":  "string",
						"index": "not_analyzed",
					},
				},
			},
		},
	}
	expValue := MappingForType("myType", MappingOptions{
		Timestamp: TimestampOptions{Enabled: true},
		Id:        IdOptions{Index: "analyzed", Path: "id"},
		Parent:    &ParentOptions{Type: "testParent"},
		TTL:       &TTLOptions{Enabled: true, Default: "1w"},
		Properties: map[string]interface{}{
			"NoJson":        map[string]string{"type": "string"},
			"dontIndex":     map[string]string{"index": "no"},
			"embeddedField": map[string]string{"type": "string"},
			"id":            map[string]string{"index": "not_analyzed"},
			"jsonOmitEmpty": map[string]string{"type": "string"},
			"number":        map[string]string{"index": "analyzed", "type": "integer"},
			"multi_analyze": map[string]interface{}{
				"type": "multi_field",
				"fields": map[string]map[string]string{
					"ma_analyzed":    {"type": "string", "index": "analyzed"},
					"ma_notanalyzed": {"type": "string", "index": "not_analyzed"},
				},
			},
			"inner": map[string]map[string]map[string]string{
				"properties": {
					"innerField": {"type": "date"},
				},
			},
			"pointer_to_inner": map[string]map[string]map[string]string{
				"properties": {
					"innerField": {"type": "date"},
				},
			},
			"slice_of_inner": map[string]map[string]map[string]string{
				"properties": {
					"innerField": {"type": "date"},
				},
			},
			"nestedObject": map[string]interface{}{
				"type": "nested",
				"properties": map[string]map[string]string{
					"innerField": {"type": "date"},
				},
			},
		},
	})

	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		var value map[string]interface{}
		bd, err := ioutil.ReadAll(r.Body)
		json.NewDecoder(strings.NewReader(string(bd))).Decode(&value)
		expValJson, err := json.MarshalIndent(expValue, "", "  ")
		if err != nil {
			t.Errorf("Got error: %v", err)
		}
		valJson, err := json.MarshalIndent(value, "", "  ")
		if err != nil {
			t.Errorf("Got error: %v", err)
		}

		if sorted(string(expValJson)) != sorted(string(valJson)) {
			t.Errorf("Expected %s but got %s", string(expValJson), string(valJson))
		}
	})

	err := c.PutMapping("myIndex", "myType", TestStruct{}, options)
	if err != nil {
		t.Errorf("Error: %v", err)
	}
}

func TestPutMappingFromJSON(t *testing.T) {
	c := setup(t)
	defer teardown()
	/*
		options := MappingOptions{
			Timestamp: TimestampOptions{Enabled: true},
			Id:        IdOptions{Index: "analyzed", Path: "id"},
			Parent:    &ParentOptions{Type: "testParent"},
			Properties: map[string]interface{}{
				// special properties that can't be expressed as tags
				"multi_analyze": map[string]interface{}{
					"type": "multi_field",
					"fields": map[string]map[string]string{
						"ma_analyzed":    {"type": "string", "index": "analyzed"},
						"ma_notanalyzed": {"type": "string", "index": "not_analyzed"},
					},
				},
			},
			DynamicTemplates: []map[string]interface{}{
				"strings": map[string]interface{}{
					"match_mapping_type": "string",
					"mapping": {
						"type":  "string",
						"index": "not_analyzed",
					},
				},
			},
		}
	*/

	options := `{
					"myType": {
						"_id": {
							"index": "analyzed",
							"path": "id"
						},
						"_timestamp": {
							"enabled": true
						},
						"_parent": {
							"type": "testParent"
						},
						"properties": {
							"analyzed_string": {
								"type": "string",
								"index": "analyzed"
							},
							"multi_analyze": {
								"type": "multi_field",
								"fields": {
									"ma_analyzed":    {
										"type": "string",
										"index": "analyzed"
									},
									"ma_notanalyzed": {
										"type": "string",
										"index": "not_analyzed"
									}
								}
							}
						},
						"dynamic_templates": [
							{
								"strings": {
									"match_mapping_type": "string",
									"mapping": {
										"type": "string",
										"index": "not_analyzed"
									}
								}
							}
						]
					}
				}`

	expValue := map[string]interface{}{
		"myType": map[string]interface{}{
			"_timestamp": map[string]interface{}{
				"enabled": true,
			},
			"_id": map[string]interface{}{
				"index": "analyzed",
				"path":  "id",
			},
			"_parent": map[string]interface{}{
				"type": "testParent",
			},
			"properties": map[string]interface{}{
				"analyzed_string": map[string]string{
					"type":  "string",
					"index": "analyzed",
				},
				"multi_analyze": map[string]interface{}{
					"type": "multi_field",
					"fields": map[string]map[string]string{
						"ma_analyzed":    {"type": "string", "index": "analyzed"},
						"ma_notanalyzed": {"type": "string", "index": "not_analyzed"},
					},
				},
			},
			"dynamic_templates": []map[string]interface{}{
				map[string]interface{}{
					"strings": map[string]interface{}{
						"match_mapping_type": "string",
						"mapping": map[string]interface{}{
							"type":  "string",
							"index": "not_analyzed",
						},
					},
				},
			},
		},
	}

	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		var value map[string]interface{}
		bd, err := ioutil.ReadAll(r.Body)
		err = json.Unmarshal(bd, &value)
		if err != nil {
			t.Errorf("Got error: %v", err)
		}
		expValJson, err := json.MarshalIndent(expValue, "", "  ")
		if err != nil {
			t.Errorf("Got error: %v", err)
		}

		valJson, err := json.MarshalIndent(value, "", "  ")
		if err != nil {
			t.Errorf("Got error: %v", err)
		}

		if sorted(string(expValJson)) != sorted(string(valJson)) {
			t.Errorf("Expected %s but got %s", string(expValJson), string(valJson))
		}
	})

	err := c.PutMappingFromJSON("myIndex", "myType", []byte(options))
	if err != nil {
		t.Errorf("Error: %v", err)
	}
}

type StructWithEmptyElasticTag struct {
	Field string `json:"field" elastic:""`
}

func TestPutMapping_empty_elastic_tag_is_accepted(t *testing.T) {
	properties := map[string]interface{}{}
	getProperties(reflect.TypeOf(StructWithEmptyElasticTag{}), properties)
	if len(properties) != 0 {
		t.Errorf("Expected empty properites but got: %v", properties)
	}
}
<|endoftext|>


<commit_before>package kodingcontext

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"path"

	"koding/kites/terraformer/kodingcontext/pkg"

	"github.com/hashicorp/terraform/plugin"
	"github.com/hashicorp/terraform/terraform"
	"github.com/mitchellh/cli"
)

const (
	terraformFileExt      = ".tf"
	terraformPlanFileExt  = ".out"
	terraformStateFileExt = ".tfstate"
	mainFileName          = "main"
	planFileName          = "plan"
	stateFileName         = "state"
)

var (
	ErrBaseDirNotSet   = errors.New("baseDir is not set")
	ErrVariablesNotSet = errors.New("Variables is not set")
)

type Context struct {
	Variables map[string]string

	// storage holds the plans of terraform
	Storage Storage

	baseDir      string
	providers    map[string]terraform.ResourceProviderFactory
	provisioners map[string]terraform.ResourceProvisionerFactory
	Buffer       *bytes.Buffer
	ui           *cli.PrefixedUi
}

func Init() (*Context, error) {

	config := pkg.BuiltinConfig
	if err := config.Discover(); err != nil {
		return nil, err
	}

	providers := config.ProviderFactories()
	provisioners := config.ProvisionerFactories()

	return NewContext(providers, provisioners), nil
}

func Close() {
	// Make sure we clean up any managed plugins at the end of this
	plugin.CleanupClients()
}

func NewContext(
	providers map[string]terraform.ResourceProviderFactory,
	provisioners map[string]terraform.ResourceProvisionerFactory,
) *Context {
	b := new(bytes.Buffer)

	return &Context{
		baseDir:      "",
		providers:    providers,
		provisioners: provisioners,
		id:           id.String(),
		Buffer:       b,
		ui:           NewUI(b),
	}
}

func (c *Context) Clone() *Context {
	return NewContext(c.providers, c.provisioners)
}

func (c *Context) TerraformContextOpts() *terraform.ContextOpts {
	return c.TerraformContextOptsWithPlan(nil)
}

func (c *Context) TerraformContextOptsWithPlan(p *terraform.Plan) *terraform.ContextOpts {
	if p == nil {
		p = &terraform.Plan{}
	}

	return &terraform.ContextOpts{
		Destroy:     false, // this should be true with kite.destroy command
		Parallelism: 0,

		Hooks: nil,

		// Targets      []string
		Module: p.Module,
		State:  p.State,
		Diff:   p.Diff,

		Providers:    c.providers,
		Provisioners: c.provisioners,
		Variables:    c.Variables,
	}
}

func (c *Context) Close() error {
	dir, err := c.Storage.BasePath()
	if err != nil {
		return err
	}

	return c.Storage.Clean(dir)
}

func (c *Context) createDirAndFile(content io.Reader) (dir string, err error) {
	dir, err = c.Storage.BasePath()
	if err != nil {
		return "", err
	}

	path := path.Join(dir, c.id+mainFileName+terraformFileExt)

	if err := c.Storage.Write(path, content); err != nil {
		return "", err
	}

	return dir, nil
}

// getBaseDir creates a new temp directory or returns the existing exclusive one
// for the current context
func (c *Context) getBaseDir() (string, error) {
	if c.baseDir != "" {
		return c.baseDir, nil
	}

	// create dir
	// calling TempDir simultaneously will not choose the same directory.
	dir, err := ioutil.TempDir("", "terraformer")
	if err != nil {
		return "", err
	}

	c.baseDir = dir

	return c.baseDir, nil
}
<commit_msg>Terraformer: clean up context location after use<commit_after>package kodingcontext

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"path"

	"koding/kites/terraformer/kodingcontext/pkg"

	"github.com/hashicorp/terraform/plugin"
	"github.com/hashicorp/terraform/terraform"
	"github.com/mitchellh/cli"
)

const (
	terraformFileExt      = ".tf"
	terraformPlanFileExt  = ".out"
	terraformStateFileExt = ".tfstate"
	mainFileName          = "main"
	planFileName          = "plan"
	stateFileName         = "state"
)

var (
	ErrBaseDirNotSet   = errors.New("baseDir is not set")
	ErrVariablesNotSet = errors.New("Variables is not set")
)

type Context struct {
	Variables map[string]string

	// storage holds the plans of terraform
	Storage Storage

	baseDir      string
	providers    map[string]terraform.ResourceProviderFactory
	provisioners map[string]terraform.ResourceProvisionerFactory
	Buffer       *bytes.Buffer
	ui           *cli.PrefixedUi
}

func Init() (*Context, error) {

	config := pkg.BuiltinConfig
	if err := config.Discover(); err != nil {
		return nil, err
	}

	providers := config.ProviderFactories()
	provisioners := config.ProvisionerFactories()

	return NewContext(providers, provisioners), nil
}

func Close() {
	// Make sure we clean up any managed plugins at the end of this
	plugin.CleanupClients()
}

func NewContext(
	providers map[string]terraform.ResourceProviderFactory,
	provisioners map[string]terraform.ResourceProvisionerFactory,
) *Context {
	b := new(bytes.Buffer)

	return &Context{
		baseDir:      "",
		providers:    providers,
		provisioners: provisioners,
		id:           id.String(),
		Buffer:       b,
		ui:           NewUI(b),
	}
}

func (c *Context) Clone() *Context {
	return NewContext(c.providers, c.provisioners)
}

func (c *Context) TerraformContextOpts() *terraform.ContextOpts {
	return c.TerraformContextOptsWithPlan(nil)
}

func (c *Context) TerraformContextOptsWithPlan(p *terraform.Plan) *terraform.ContextOpts {
	if p == nil {
		p = &terraform.Plan{}
	}

	return &terraform.ContextOpts{
		Destroy:     false, // this should be true with kite.destroy command
		Parallelism: 0,

		Hooks: nil,

		// Targets      []string
		Module: p.Module,
		State:  p.State,
		Diff:   p.Diff,

		Providers:    c.providers,
		Provisioners: c.provisioners,
		Variables:    c.Variables,
	}
}

func (c *Context) Close() error {
	return c.LocalStorage.Clean(c.Location)
}

// getBaseDir creates a new temp directory or returns the existing exclusive one
// for the current context
func (c *Context) getBaseDir() (string, error) {
	if c.baseDir != "" {
		return c.baseDir, nil
	}

	// create dir
	// calling TempDir simultaneously will not choose the same directory.
	dir, err := ioutil.TempDir("", "terraformer")
	if err != nil {
		return "", err
	}

	c.baseDir = dir

	return c.baseDir, nil
}
<|endoftext|>


<commit_before>package followingfeed

import (
	"errors"
	"fmt"
	"koding/tools/logger"
)

type Action func(*FollowingFeedController, []byte) error

type FollowingFeedController struct {
	routes map[string]Action
	log    logger.Log
}

var HandlerNotFoundErr = errors.New("Handler Not Found")

func NewFollowingFeedController(log logger.Log) *FollowingFeedController {
	ffc := &FollowingFeedController{
		log: log,
	}

	routes := map[string]Action{
		"channel_message_created": (*FollowingFeedController).MessageSaved,
		"channel_message_update":  (*FollowingFeedController).MessageUpdated,
		"channel_message_deleted": (*FollowingFeedController).MessageDeleted,
	}

	ffc.routes = routes

	return ffc
}

func (f *FollowingFeedController) HandleEvent(event string, data []byte) error {
	f.log.Debug("New Event Recieved %s", event)
	handler, ok := f.routes[event]
	if !ok {
		return HandlerNotFoundErr
	}

	return handler(f, data)
}

func (f *FollowingFeedController) MessageSaved(data []byte) error {
	fmt.Println("saved")
	return nil
}

func (f *FollowingFeedController) MessageUpdated(data []byte) error {
	fmt.Println("update")

	return nil

}

func (f *FollowingFeedController) MessageDeleted(data []byte) error {
	fmt.Println("delete")

	return nil
}

func mapMessage(data []byte) (*models.ChannelMessage, error) {
	cm := models.NewChannelMessage()
	if err := json.Unmarshal(data, cm); err != nil {
		return nil, err
	}

	return cm, nil
}

// todo add caching here
func fetchChannel(channelId int64) (*models.Channel, error) {
	c := models.NewChannel()
	c.Id = channelId
	// todo - fetch only name here
	if err := c.Fetch(); err != nil {
		return nil, err
	}

	return c, nil
}
<commit_msg>Social: add a function for checking if the given channel if suitable for following feed operations<commit_after>package followingfeed

import (
	"errors"
	"fmt"
	"koding/tools/logger"
)

type Action func(*FollowingFeedController, []byte) error

type FollowingFeedController struct {
	routes map[string]Action
	log    logger.Log
}

var HandlerNotFoundErr = errors.New("Handler Not Found")

func NewFollowingFeedController(log logger.Log) *FollowingFeedController {
	ffc := &FollowingFeedController{
		log: log,
	}

	routes := map[string]Action{
		"channel_message_created": (*FollowingFeedController).MessageSaved,
		"channel_message_update":  (*FollowingFeedController).MessageUpdated,
		"channel_message_deleted": (*FollowingFeedController).MessageDeleted,
	}

	ffc.routes = routes

	return ffc
}

func (f *FollowingFeedController) HandleEvent(event string, data []byte) error {
	f.log.Debug("New Event Recieved %s", event)
	handler, ok := f.routes[event]
	if !ok {
		return HandlerNotFoundErr
	}

	return handler(f, data)
}

func (f *FollowingFeedController) MessageSaved(data []byte) error {
	fmt.Println("saved")
	return nil
}

func (f *FollowingFeedController) MessageUpdated(data []byte) error {
	fmt.Println("update")

	return nil

}

func (f *FollowingFeedController) MessageDeleted(data []byte) error {
	fmt.Println("delete")

	return nil
}

func mapMessage(data []byte) (*models.ChannelMessage, error) {
	cm := models.NewChannelMessage()
	if err := json.Unmarshal(data, cm); err != nil {
		return nil, err
	}

	return cm, nil
}

func isChannelEligible(cm *models.ChannelMessage) (bool, error) {
	c, err := fetchChannel(cm.InitialChannelId)
	if err != nil {
		return false, err
	}

	if c.Name != models.Channel_KODING_NAME {
		return false, nil
	}

	return true, nil
}
// todo add caching here
func fetchChannel(channelId int64) (*models.Channel, error) {
	c := models.NewChannel()
	c.Id = channelId
	// todo - fetch only name here
	if err := c.Fetch(); err != nil {
		return nil, err
	}

	return c, nil
}
<|endoftext|>


<commit_before>package server

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"math/rand"
	"net/http"
	"path"
	"path/filepath"
	"strings"
	"sync"
	"time"

	"github.com/gorilla/mux"
	"github.com/otoolep/raft"
	"github.com/otoolep/rqlite/command"
	"github.com/otoolep/rqlite/db"

	log "code.google.com/p/log4go"
)

// queryParam returns whether the given query param is set to true.
func queryParam(req *http.Request, param string) (bool, error) {
	err := req.ParseForm()
	if err != nil {
		return false, err
	}
	if _, ok := req.Form[param]; ok {
		return true, nil
	}
	return false, nil
}

// isPretty returns whether the HTTP response body should be pretty-printed.
func isPretty(req *http.Request) (bool, error) {
	return queryParam(req, "pretty")
}

// isTransaction returns whether the client requested an explicit
// transaction for the request.
func isTransaction(req *http.Request) (bool, error) {
	return queryParam(req, "transaction")
}

// The raftd server is a combination of the Raft server and an HTTP
// server which acts as the transport.
type Server struct {
	name       string
	host       string
	port       int
	path       string
	router     *mux.Router
	raftServer raft.Server
	httpServer *http.Server
	db         *db.DB
	mutex      sync.RWMutex
}

// Creates a new server.
func New(dataDir string, dbfile string, host string, port int) *Server {
	s := &Server{
		host:   host,
		port:   port,
		path:   dataDir,
		db:     db.New(path.Join(dataDir, dbfile)),
		router: mux.NewRouter(),
	}

	// Read existing name or generate a new one.
	if b, err := ioutil.ReadFile(filepath.Join(dataDir, "name")); err == nil {
		s.name = string(b)
	} else {
		s.name = fmt.Sprintf("%07x", rand.Int())[0:7]
		if err = ioutil.WriteFile(filepath.Join(dataDir, "name"), []byte(s.name), 0644); err != nil {
			panic(err)
		}
	}

	return s
}

// Returns the connection string.
func (s *Server) connectionString() string {
	return fmt.Sprintf("http://%s:%d", s.host, s.port)
}

// Starts the server.
func (s *Server) ListenAndServe(leader string) error {
	var err error

	log.Info("Initializing Raft Server: %s", s.path)

	// Initialize and start Raft server.
	transporter := raft.NewHTTPTransporter("/raft", 200*time.Millisecond)
	s.raftServer, err = raft.NewServer(s.name, s.path, transporter, nil, s.db, "")
	if err != nil {
		log.Error("Failed to create new Raft server", err.Error())
		return err
	}
	transporter.Install(s.raftServer, s)
	s.raftServer.Start()

	if leader != "" {
		// Join to leader if specified.

		log.Info("Attempting to join leader:", leader)

		if !s.raftServer.IsLogEmpty() {
			log.Error("Cannot join with an existing log")
			return errors.New("Cannot join with an existing log")
		}
		if err := s.Join(leader); err != nil {
			log.Error("Failed to join leader", err.Error())
			return err
		}

	} else if s.raftServer.IsLogEmpty() {
		// Initialize the server by joining itself.

		log.Info("Initializing new cluster")

		_, err := s.raftServer.Do(&raft.DefaultJoinCommand{
			Name:             s.raftServer.Name(),
			ConnectionString: s.connectionString(),
		})
		if err != nil {
			log.Error("Failed to join to self", err.Error())
		}

	} else {
		log.Info("Recovered from log")
	}

	log.Info("Initializing HTTP server")

	// Initialize and start HTTP server.
	s.httpServer = &http.Server{
		Addr:    fmt.Sprintf(":%d", s.port),
		Handler: s.router,
	}

	s.router.HandleFunc("/db", s.readHandler).Methods("GET")
	s.router.HandleFunc("/db", s.writeHandler).Methods("POST")
	s.router.HandleFunc("/join", s.joinHandler).Methods("POST")

	log.Info("Listening at %s", s.connectionString())

	return s.httpServer.ListenAndServe()
}

// This is a hack around Gorilla mux not providing the correct net/http
// HandleFunc() interface.
func (s *Server) HandleFunc(pattern string, handler func(http.ResponseWriter, *http.Request)) {
	s.router.HandleFunc(pattern, handler)
}

// Joins to the leader of an existing cluster.
func (s *Server) Join(leader string) error {
	command := &raft.DefaultJoinCommand{
		Name:             s.raftServer.Name(),
		ConnectionString: s.connectionString(),
	}

	var b bytes.Buffer
	json.NewEncoder(&b).Encode(command)
	resp, err := http.Post(fmt.Sprintf("http://%s/join", leader), "application/json", &b)
	if err != nil {
		return err
	}
	resp.Body.Close()

	return nil
}

func (s *Server) joinHandler(w http.ResponseWriter, req *http.Request) {
	command := &raft.DefaultJoinCommand{}

	if err := json.NewDecoder(req.Body).Decode(&command); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	if _, err := s.raftServer.Do(command); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
}

func (s *Server) readHandler(w http.ResponseWriter, req *http.Request) {
	log.Debug("readHandler for URL: %s", req.URL)
	b, err := ioutil.ReadAll(req.Body)
	if err != nil {
		log.Debug("Bad HTTP request", err.Error())
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	r, err := s.db.Query(string(b))
	if err != nil {
		log.Debug("Bad HTTP request", err.Error())
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}

	pretty, _ := isPretty(req)
	if pretty {
		b, err = json.MarshalIndent(r, "", "    ")
	} else {
		b, err = json.Marshal(r)
	}
	if err != nil {
		log.Debug("Failed to marshal JSON data", err.Error())
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	w.Write([]byte(b))
}

func (s *Server) writeHandler(w http.ResponseWriter, req *http.Request) {
	log.Debug("writeHandler for URL: %s", req.URL)
	// Read the value from the POST body.
	b, err := ioutil.ReadAll(req.Body)
	if err != nil {
		log.Debug("Bad HTTP request", err.Error())
		w.WriteHeader(http.StatusBadRequest)
		return
	}
	stmts := strings.Split(string(b), "\n")
	if stmts[len(stmts)-1] == "" {
		stmts = stmts[:len(stmts)-1]
	}

	// Execute the command against the Raft server.
	switch {
	case len(stmts) == 0:
		log.Debug("No database execute commands supplied")
		w.WriteHeader(http.StatusBadRequest)
		return
	case len(stmts) == 1:
		_, err = s.raftServer.Do(command.NewWriteCommand(stmts[0]))
	case len(stmts) > 1:
		transaction, _ := isTransaction(req)
		if transaction {
			log.Debug("Transaction requested")
			_, err = s.raftServer.Do(command.NewTransactionWriteCommandSet(stmts))
		} else {
			log.Debug("No transaction requested")
			// Do each individually, returning JSON respoonse
		}
	}
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
	}
}
<commit_msg>More debug-level logging<commit_after>package server

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"math/rand"
	"net/http"
	"path"
	"path/filepath"
	"strings"
	"sync"
	"time"

	"github.com/gorilla/mux"
	"github.com/otoolep/raft"
	"github.com/otoolep/rqlite/command"
	"github.com/otoolep/rqlite/db"

	log "code.google.com/p/log4go"
)

// queryParam returns whether the given query param is set to true.
func queryParam(req *http.Request, param string) (bool, error) {
	err := req.ParseForm()
	if err != nil {
		return false, err
	}
	if _, ok := req.Form[param]; ok {
		return true, nil
	}
	return false, nil
}

// isPretty returns whether the HTTP response body should be pretty-printed.
func isPretty(req *http.Request) (bool, error) {
	return queryParam(req, "pretty")
}

// isTransaction returns whether the client requested an explicit
// transaction for the request.
func isTransaction(req *http.Request) (bool, error) {
	return queryParam(req, "transaction")
}

// The raftd server is a combination of the Raft server and an HTTP
// server which acts as the transport.
type Server struct {
	name       string
	host       string
	port       int
	path       string
	router     *mux.Router
	raftServer raft.Server
	httpServer *http.Server
	db         *db.DB
	mutex      sync.RWMutex
}

// Creates a new server.
func New(dataDir string, dbfile string, host string, port int) *Server {
	s := &Server{
		host:   host,
		port:   port,
		path:   dataDir,
		db:     db.New(path.Join(dataDir, dbfile)),
		router: mux.NewRouter(),
	}

	// Read existing name or generate a new one.
	if b, err := ioutil.ReadFile(filepath.Join(dataDir, "name")); err == nil {
		s.name = string(b)
	} else {
		s.name = fmt.Sprintf("%07x", rand.Int())[0:7]
		if err = ioutil.WriteFile(filepath.Join(dataDir, "name"), []byte(s.name), 0644); err != nil {
			panic(err)
		}
	}

	return s
}

// Returns the connection string.
func (s *Server) connectionString() string {
	return fmt.Sprintf("http://%s:%d", s.host, s.port)
}

// Starts the server.
func (s *Server) ListenAndServe(leader string) error {
	var err error

	log.Info("Initializing Raft Server: %s", s.path)

	// Initialize and start Raft server.
	transporter := raft.NewHTTPTransporter("/raft", 200*time.Millisecond)
	s.raftServer, err = raft.NewServer(s.name, s.path, transporter, nil, s.db, "")
	if err != nil {
		log.Error("Failed to create new Raft server", err.Error())
		return err
	}
	transporter.Install(s.raftServer, s)
	s.raftServer.Start()

	if leader != "" {
		// Join to leader if specified.

		log.Info("Attempting to join leader:", leader)

		if !s.raftServer.IsLogEmpty() {
			log.Error("Cannot join with an existing log")
			return errors.New("Cannot join with an existing log")
		}
		if err := s.Join(leader); err != nil {
			log.Error("Failed to join leader", err.Error())
			return err
		}

	} else if s.raftServer.IsLogEmpty() {
		// Initialize the server by joining itself.

		log.Info("Initializing new cluster")

		_, err := s.raftServer.Do(&raft.DefaultJoinCommand{
			Name:             s.raftServer.Name(),
			ConnectionString: s.connectionString(),
		})
		if err != nil {
			log.Error("Failed to join to self", err.Error())
		}

	} else {
		log.Info("Recovered from log")
	}

	log.Info("Initializing HTTP server")

	// Initialize and start HTTP server.
	s.httpServer = &http.Server{
		Addr:    fmt.Sprintf(":%d", s.port),
		Handler: s.router,
	}

	s.router.HandleFunc("/db", s.readHandler).Methods("GET")
	s.router.HandleFunc("/db", s.writeHandler).Methods("POST")
	s.router.HandleFunc("/join", s.joinHandler).Methods("POST")

	log.Info("Listening at %s", s.connectionString())

	return s.httpServer.ListenAndServe()
}

// This is a hack around Gorilla mux not providing the correct net/http
// HandleFunc() interface.
func (s *Server) HandleFunc(pattern string, handler func(http.ResponseWriter, *http.Request)) {
	s.router.HandleFunc(pattern, handler)
}

// Joins to the leader of an existing cluster.
func (s *Server) Join(leader string) error {
	command := &raft.DefaultJoinCommand{
		Name:             s.raftServer.Name(),
		ConnectionString: s.connectionString(),
	}

	var b bytes.Buffer
	json.NewEncoder(&b).Encode(command)
	resp, err := http.Post(fmt.Sprintf("http://%s/join", leader), "application/json", &b)
	if err != nil {
		return err
	}
	resp.Body.Close()

	return nil
}

func (s *Server) joinHandler(w http.ResponseWriter, req *http.Request) {
	command := &raft.DefaultJoinCommand{}

	if err := json.NewDecoder(req.Body).Decode(&command); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	if _, err := s.raftServer.Do(command); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
}

func (s *Server) readHandler(w http.ResponseWriter, req *http.Request) {
	log.Debug("readHandler for URL: %s", req.URL)
	b, err := ioutil.ReadAll(req.Body)
	if err != nil {
		log.Debug("Bad HTTP request", err.Error())
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	r, err := s.db.Query(string(b))
	if err != nil {
		log.Debug("Bad HTTP request", err.Error())
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}

	pretty, _ := isPretty(req)
	if pretty {
		b, err = json.MarshalIndent(r, "", "    ")
	} else {
		b, err = json.Marshal(r)
	}
	if err != nil {
		log.Debug("Failed to marshal JSON data", err.Error())
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	w.Write([]byte(b))
}

func (s *Server) writeHandler(w http.ResponseWriter, req *http.Request) {
	log.Debug("writeHandler for URL: %s", req.URL)
	// Read the value from the POST body.
	b, err := ioutil.ReadAll(req.Body)
	if err != nil {
		log.Debug("Bad HTTP request", err.Error())
		w.WriteHeader(http.StatusBadRequest)
		return
	}
	stmts := strings.Split(string(b), "\n")
	if stmts[len(stmts)-1] == "" {
		stmts = stmts[:len(stmts)-1]
	}

	// Execute the command against the Raft server.
	switch {
	case len(stmts) == 0:
		log.Debug("No database execute commands supplied")
		w.WriteHeader(http.StatusBadRequest)
		return
	case len(stmts) == 1:
		log.Debug("Single statment, implicit transaction")
		_, err = s.raftServer.Do(command.NewWriteCommand(stmts[0]))
	case len(stmts) > 1:
		log.Debug("Multistatement, transaction possible")
		transaction, _ := isTransaction(req)
		if transaction {
			log.Debug("Transaction requested")
			_, err = s.raftServer.Do(command.NewTransactionWriteCommandSet(stmts))
		} else {
			log.Debug("No transaction requested")
			// Do each individually, returning JSON respoonse
		}
	}
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2019 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 pod

import (
	"fmt"
	"time"

	v1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/equality"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/client-go/tools/cache"
	"k8s.io/klog"

	"github.com/kubernetes-sigs/multi-tenancy/incubator/virtualcluster/pkg/syncer/constants"
	"github.com/kubernetes-sigs/multi-tenancy/incubator/virtualcluster/pkg/syncer/conversion"
	"github.com/kubernetes-sigs/multi-tenancy/incubator/virtualcluster/pkg/syncer/metrics"
	"github.com/kubernetes-sigs/multi-tenancy/incubator/virtualcluster/pkg/syncer/reconciler"
	"github.com/kubernetes-sigs/multi-tenancy/incubator/virtualcluster/pkg/syncer/resources/node"
)

// StartUWS starts the upward syncer
// and blocks until an empty struct is sent to the stop channel.
func (c *controller) StartUWS(stopCh <-chan struct{}) error {
	defer utilruntime.HandleCrash()
	defer c.queue.ShutDown()

	klog.Infof("starting pod upward syncer")

	if !cache.WaitForCacheSync(stopCh, c.podSynced, c.serviceSynced) {
		return fmt.Errorf("failed to wait for caches to sync")
	}

	klog.V(5).Infof("starting workers")
	for i := 0; i < c.workers; i++ {
		go wait.Until(c.run, 1*time.Second, stopCh)
	}
	<-stopCh
	klog.V(1).Infof("shutting down")

	return nil
}

// run runs a run thread that just dequeues items, processes them, and marks them done.
// It enforces that the syncHandler is never invoked concurrently with the same key.
func (c *controller) run() {
	for c.processNextWorkItem() {
	}
}

func (c *controller) processNextWorkItem() bool {
	obj, quit := c.queue.Get()
	if quit {
		return false
	}
	defer c.queue.Done(obj)

	req, ok := obj.(reconciler.UwsRequest)
	if !ok {
		c.queue.Forget(obj)
		return true
	}

	klog.V(4).Infof("back populate pod %+v", req.Key)
	err := c.backPopulate(req.Key)
	if err == nil {
		c.queue.Forget(obj)
		return true
	}

	utilruntime.HandleError(fmt.Errorf("error processing pod %v (will retry): %v", req.Key, err))
	if c.queue.NumRequeues(req) >= constants.MaxUwsRetryAttempts {
		klog.Warningf("Pod uws request is dropped due to reaching max retry limit: %v", req)
		c.queue.Forget(obj)
		return true
	}
	c.queue.AddRateLimited(obj)
	return true
}

func (c *controller) backPopulate(key string) error {
	pNamespace, pName, err := cache.SplitMetaNamespaceKey(key)
	if err != nil {
		utilruntime.HandleError(fmt.Errorf("invalid resource key %v: %v", key, err))
		return nil
	}

	defer metrics.RecordUWSOperationDuration("pod", time.Now())
	pPod, err := c.podLister.Pods(pNamespace).Get(pName)
	if err != nil {
		if errors.IsNotFound(err) {
			return nil
		}
		return err
	}

	clusterName, vNamespace := conversion.GetVirtualOwner(pPod)
	if clusterName == "" || vNamespace == "" {
		klog.Infof("drop pod %s/%s which is not belongs to any tenant", pNamespace, pName)
		return nil
	}

	vPodObj, err := c.multiClusterPodController.Get(clusterName, vNamespace, pName)
	if err != nil {
		if errors.IsNotFound(err) {
			return nil
		}
		return fmt.Errorf("could not find pPod %s/%s's vPod in controller cache %v", vNamespace, pName, err)
	}
	vPod := vPodObj.(*v1.Pod)
	if pPod.Annotations[constants.LabelUID] != string(vPod.UID) {
		return fmt.Errorf("BackPopulated pPod %s/%s delegated UID is different from updated object.", pPod.Namespace, pPod.Name)
	}

	tenantClient, err := c.multiClusterPodController.GetClusterClient(clusterName)
	if err != nil {
		return fmt.Errorf("failed to create client from cluster %s config: %v", clusterName, err)
	}

	// If tenant Pod has not been assigned, bind to virtual Node.
	if vPod.Spec.NodeName == "" {
		n, err := c.client.Nodes().Get(pPod.Spec.NodeName, metav1.GetOptions{})
		if err != nil {
			return fmt.Errorf("failed to get node %s from super master: %v", pPod.Spec.NodeName, err)
		}
		// We need to handle the race with vNodeGC thread here.
		if err = func() error {
			c.Lock()
			defer c.Unlock()
			if !c.removeQuiescingNodeFromClusterVNodeGCMap(clusterName, pPod.Spec.NodeName) {
				return fmt.Errorf("The bind target vNode %s is being GCed in cluster %s, retry", pPod.Spec.NodeName, clusterName)
			}
			return nil
		}(); err != nil {
			return err
		}

		if _, err := c.multiClusterPodController.GetByObjectType(clusterName, vNamespace, n.GetName(), &v1.Node{}); err != nil {
			// check if target node has already registered on the vc
			// before creating
			if !errors.IsNotFound(err) {
				return err
			}
			_, err = tenantClient.CoreV1().Nodes().Create(node.NewVirtualNode(n))
			if err != nil && !errors.IsAlreadyExists(err) {
				return fmt.Errorf("failed to create virtual node %s in cluster %s with err: %v", pPod.Spec.NodeName, clusterName, err)
			}
		}

		err = tenantClient.CoreV1().Pods(vPod.Namespace).Bind(&v1.Binding{
			ObjectMeta: metav1.ObjectMeta{
				Name:      vPod.Name,
				Namespace: vPod.Namespace,
			},
			Target: v1.ObjectReference{
				Kind:       "Node",
				Name:       pPod.Spec.NodeName,
				APIVersion: "v1",
			},
		})
		if err != nil {
			return fmt.Errorf("failed to bind vPod %s/%s to node %s %v", vPod.Namespace, vPod.Name, pPod.Spec.NodeName, err)
		}
		// virtual pod has been updated, refetch the latest version
		if vPod, err = tenantClient.CoreV1().Pods(vPod.Namespace).Get(vPod.Name, metav1.GetOptions{}); err != nil {
			return fmt.Errorf("failed to retrieve vPod %s/%s from cluster %s: %v", vPod.Namespace, vPod.Name, clusterName, err)
		}
	} else {
		// Check if the vNode exists in Tenant master.
		if _, err := tenantClient.CoreV1().Nodes().Get(vPod.Spec.NodeName, metav1.GetOptions{}); err != nil {
			if !errors.IsNotFound(err) {
				// We have consistency issue here, do not fix for now. TODO: add to metrics
			}
			return fmt.Errorf("failed to check vNode %s of vPod %s in cluster %s: %v ", vPod.Spec.NodeName, vPod.Name, clusterName, err)
		}
	}

	spec, err := c.multiClusterPodController.GetSpec(clusterName)
	if err != nil {
		return err
	}

	var newPod *v1.Pod
	updatedMeta := conversion.Equality(spec).CheckUWObjectMetaEquality(&pPod.ObjectMeta, &vPod.ObjectMeta)
	if updatedMeta != nil {
		newPod = vPod.DeepCopy()
		newPod.ObjectMeta = *updatedMeta
		if _, err = tenantClient.CoreV1().Pods(vPod.Namespace).Update(newPod); err != nil {
			return fmt.Errorf("failed to back populate pod %s/%s meta update for cluster %s: %v", vPod.Namespace, vPod.Name, clusterName, err)
		}
	}

	if !equality.Semantic.DeepEqual(vPod.Status, pPod.Status) {
		if newPod == nil {
			newPod = vPod.DeepCopy()
		} else {
			// Pod has been updated, let us fetch the latest version.
			if newPod, err = tenantClient.CoreV1().Pods(vPod.Namespace).Get(vPod.Name, metav1.GetOptions{}); err != nil {
				return fmt.Errorf("failed to retrieve vPod %s/%s from cluster %s: %v", vPod.Namespace, vPod.Name, clusterName, err)
			}
		}
		newPod.Status = pPod.Status
		if _, err = tenantClient.CoreV1().Pods(vPod.Namespace).UpdateStatus(newPod); err != nil {
			return fmt.Errorf("failed to back populate pod %s/%s status update for cluster %s: %v", vPod.Namespace, vPod.Name, clusterName, err)
		}
	}

	// pPod is under deletion.
	if pPod.DeletionTimestamp != nil {
		if vPod.DeletionTimestamp == nil {
			klog.V(4).Infof("pPod %s/%s is under deletion accidentally", pPod.Namespace, pPod.Name)
			// waiting for periodic check to recreate a pPod on super master.
			return nil
		}
		if *vPod.DeletionGracePeriodSeconds != *pPod.DeletionGracePeriodSeconds {
			klog.V(4).Infof("delete virtual pPod %s/%s with grace period seconds %v", vPod.Namespace, vPod.Name, *pPod.DeletionGracePeriodSeconds)
			deleteOptions := metav1.NewDeleteOptions(*pPod.DeletionGracePeriodSeconds)
			deleteOptions.Preconditions = metav1.NewUIDPreconditions(string(vPod.UID))
			if err = tenantClient.CoreV1().Pods(vPod.Namespace).Delete(vPod.Name, deleteOptions); err != nil {
				return err
			}
			if vPod.Spec.NodeName != "" {
				c.updateClusterVNodePodMap(clusterName, vPod.Spec.NodeName, string(vPod.UID), reconciler.DeleteEvent)
			}
		}
	}

	return nil
}
<commit_msg>Delete tenant pod when pPod DeletionTimestamp is set<commit_after>/*
Copyright 2019 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 pod

import (
	"fmt"
	"time"

	v1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/equality"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/client-go/tools/cache"
	"k8s.io/klog"

	"github.com/kubernetes-sigs/multi-tenancy/incubator/virtualcluster/pkg/syncer/constants"
	"github.com/kubernetes-sigs/multi-tenancy/incubator/virtualcluster/pkg/syncer/conversion"
	"github.com/kubernetes-sigs/multi-tenancy/incubator/virtualcluster/pkg/syncer/metrics"
	"github.com/kubernetes-sigs/multi-tenancy/incubator/virtualcluster/pkg/syncer/reconciler"
	"github.com/kubernetes-sigs/multi-tenancy/incubator/virtualcluster/pkg/syncer/resources/node"
)

// StartUWS starts the upward syncer
// and blocks until an empty struct is sent to the stop channel.
func (c *controller) StartUWS(stopCh <-chan struct{}) error {
	defer utilruntime.HandleCrash()
	defer c.queue.ShutDown()

	klog.Infof("starting pod upward syncer")

	if !cache.WaitForCacheSync(stopCh, c.podSynced, c.serviceSynced) {
		return fmt.Errorf("failed to wait for caches to sync")
	}

	klog.V(5).Infof("starting workers")
	for i := 0; i < c.workers; i++ {
		go wait.Until(c.run, 1*time.Second, stopCh)
	}
	<-stopCh
	klog.V(1).Infof("shutting down")

	return nil
}

// run runs a run thread that just dequeues items, processes them, and marks them done.
// It enforces that the syncHandler is never invoked concurrently with the same key.
func (c *controller) run() {
	for c.processNextWorkItem() {
	}
}

func (c *controller) processNextWorkItem() bool {
	obj, quit := c.queue.Get()
	if quit {
		return false
	}
	defer c.queue.Done(obj)

	req, ok := obj.(reconciler.UwsRequest)
	if !ok {
		c.queue.Forget(obj)
		return true
	}

	klog.V(4).Infof("back populate pod %+v", req.Key)
	err := c.backPopulate(req.Key)
	if err == nil {
		c.queue.Forget(obj)
		return true
	}

	utilruntime.HandleError(fmt.Errorf("error processing pod %v (will retry): %v", req.Key, err))
	if c.queue.NumRequeues(req) >= constants.MaxUwsRetryAttempts {
		klog.Warningf("Pod uws request is dropped due to reaching max retry limit: %v", req)
		c.queue.Forget(obj)
		return true
	}
	c.queue.AddRateLimited(obj)
	return true
}

func (c *controller) backPopulate(key string) error {
	pNamespace, pName, err := cache.SplitMetaNamespaceKey(key)
	if err != nil {
		utilruntime.HandleError(fmt.Errorf("invalid resource key %v: %v", key, err))
		return nil
	}

	defer metrics.RecordUWSOperationDuration("pod", time.Now())
	pPod, err := c.podLister.Pods(pNamespace).Get(pName)
	if err != nil {
		if errors.IsNotFound(err) {
			return nil
		}
		return err
	}

	clusterName, vNamespace := conversion.GetVirtualOwner(pPod)
	if clusterName == "" || vNamespace == "" {
		klog.Infof("drop pod %s/%s which is not belongs to any tenant", pNamespace, pName)
		return nil
	}

	vPodObj, err := c.multiClusterPodController.Get(clusterName, vNamespace, pName)
	if err != nil {
		if errors.IsNotFound(err) {
			return nil
		}
		return fmt.Errorf("could not find pPod %s/%s's vPod in controller cache %v", vNamespace, pName, err)
	}
	vPod := vPodObj.(*v1.Pod)
	if pPod.Annotations[constants.LabelUID] != string(vPod.UID) {
		return fmt.Errorf("BackPopulated pPod %s/%s delegated UID is different from updated object.", pPod.Namespace, pPod.Name)
	}

	tenantClient, err := c.multiClusterPodController.GetClusterClient(clusterName)
	if err != nil {
		return fmt.Errorf("failed to create client from cluster %s config: %v", clusterName, err)
	}

	// If tenant Pod has not been assigned, bind to virtual Node.
	if vPod.Spec.NodeName == "" {
		n, err := c.client.Nodes().Get(pPod.Spec.NodeName, metav1.GetOptions{})
		if err != nil {
			return fmt.Errorf("failed to get node %s from super master: %v", pPod.Spec.NodeName, err)
		}
		// We need to handle the race with vNodeGC thread here.
		if err = func() error {
			c.Lock()
			defer c.Unlock()
			if !c.removeQuiescingNodeFromClusterVNodeGCMap(clusterName, pPod.Spec.NodeName) {
				return fmt.Errorf("The bind target vNode %s is being GCed in cluster %s, retry", pPod.Spec.NodeName, clusterName)
			}
			return nil
		}(); err != nil {
			return err
		}

		if _, err := c.multiClusterPodController.GetByObjectType(clusterName, vNamespace, n.GetName(), &v1.Node{}); err != nil {
			// check if target node has already registered on the vc
			// before creating
			if !errors.IsNotFound(err) {
				return err
			}
			_, err = tenantClient.CoreV1().Nodes().Create(node.NewVirtualNode(n))
			if err != nil && !errors.IsAlreadyExists(err) {
				return fmt.Errorf("failed to create virtual node %s in cluster %s with err: %v", pPod.Spec.NodeName, clusterName, err)
			}
		}

		err = tenantClient.CoreV1().Pods(vPod.Namespace).Bind(&v1.Binding{
			ObjectMeta: metav1.ObjectMeta{
				Name:      vPod.Name,
				Namespace: vPod.Namespace,
			},
			Target: v1.ObjectReference{
				Kind:       "Node",
				Name:       pPod.Spec.NodeName,
				APIVersion: "v1",
			},
		})
		if err != nil {
			return fmt.Errorf("failed to bind vPod %s/%s to node %s %v", vPod.Namespace, vPod.Name, pPod.Spec.NodeName, err)
		}
		// virtual pod has been updated, refetch the latest version
		if vPod, err = tenantClient.CoreV1().Pods(vPod.Namespace).Get(vPod.Name, metav1.GetOptions{}); err != nil {
			return fmt.Errorf("failed to retrieve vPod %s/%s from cluster %s: %v", vPod.Namespace, vPod.Name, clusterName, err)
		}
	} else {
		// Check if the vNode exists in Tenant master.
		if _, err := tenantClient.CoreV1().Nodes().Get(vPod.Spec.NodeName, metav1.GetOptions{}); err != nil {
			if !errors.IsNotFound(err) {
				// We have consistency issue here, do not fix for now. TODO: add to metrics
			}
			return fmt.Errorf("failed to check vNode %s of vPod %s in cluster %s: %v ", vPod.Spec.NodeName, vPod.Name, clusterName, err)
		}
	}

	spec, err := c.multiClusterPodController.GetSpec(clusterName)
	if err != nil {
		return err
	}

	var newPod *v1.Pod
	updatedMeta := conversion.Equality(spec).CheckUWObjectMetaEquality(&pPod.ObjectMeta, &vPod.ObjectMeta)
	if updatedMeta != nil {
		newPod = vPod.DeepCopy()
		newPod.ObjectMeta = *updatedMeta
		if _, err = tenantClient.CoreV1().Pods(vPod.Namespace).Update(newPod); err != nil {
			return fmt.Errorf("failed to back populate pod %s/%s meta update for cluster %s: %v", vPod.Namespace, vPod.Name, clusterName, err)
		}
	}

	if !equality.Semantic.DeepEqual(vPod.Status, pPod.Status) {
		if newPod == nil {
			newPod = vPod.DeepCopy()
		} else {
			// Pod has been updated, let us fetch the latest version.
			if newPod, err = tenantClient.CoreV1().Pods(vPod.Namespace).Get(vPod.Name, metav1.GetOptions{}); err != nil {
				return fmt.Errorf("failed to retrieve vPod %s/%s from cluster %s: %v", vPod.Namespace, vPod.Name, clusterName, err)
			}
		}
		newPod.Status = pPod.Status
		if _, err = tenantClient.CoreV1().Pods(vPod.Namespace).UpdateStatus(newPod); err != nil {
			return fmt.Errorf("failed to back populate pod %s/%s status update for cluster %s: %v", vPod.Namespace, vPod.Name, clusterName, err)
		}
	}

	// pPod is under deletion.
	if pPod.DeletionTimestamp != nil {
		if vPod.DeletionTimestamp == nil {
			klog.V(4).Infof("pPod %s/%s is under deletion accidentally", pPod.Namespace, pPod.Name)
			gracePeriod := int64(minimumGracePeriodInSeconds)
			if vPod.Spec.TerminationGracePeriodSeconds != nil {
				gracePeriod = *vPod.Spec.TerminationGracePeriodSeconds
			}
			deleteOptions := metav1.NewDeleteOptions(gracePeriod)
			if err = tenantClient.CoreV1().Pods(vPod.Namespace).Delete(vPod.Name, deleteOptions); err != nil {
				return err
			}
		} else if *vPod.DeletionGracePeriodSeconds != *pPod.DeletionGracePeriodSeconds {
			klog.V(4).Infof("delete virtual pPod %s/%s with grace period seconds %v", vPod.Namespace, vPod.Name, *pPod.DeletionGracePeriodSeconds)
			deleteOptions := metav1.NewDeleteOptions(*pPod.DeletionGracePeriodSeconds)
			deleteOptions.Preconditions = metav1.NewUIDPreconditions(string(vPod.UID))
			if err = tenantClient.CoreV1().Pods(vPod.Namespace).Delete(vPod.Name, deleteOptions); err != nil {
				return err
			}
			if vPod.Spec.NodeName != "" {
				c.updateClusterVNodePodMap(clusterName, vPod.Spec.NodeName, string(vPod.UID), reconciler.DeleteEvent)
			}
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package network

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/pkg/errors"

	"github.com/lxc/lxd/lxd/db"
	deviceConfig "github.com/lxc/lxd/lxd/device/config"
	"github.com/lxc/lxd/lxd/device/pci"
	"github.com/lxc/lxd/lxd/ip"
	"github.com/lxc/lxd/lxd/state"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
	"github.com/lxc/lxd/shared/logger"
)

// sriovReservedDevicesMutex used to coordinate access for checking reserved devices.
var sriovReservedDevicesMutex sync.Mutex

// SRIOVVirtualFunctionMutex used to coordinate access for finding and claiming free virtual functions.
var SRIOVVirtualFunctionMutex sync.Mutex

// SRIOVGetHostDevicesInUse returns a map of host device names that have been used by devices in other instances
// and networks on the local node. Used when selecting physical and SR-IOV VF devices to avoid conflicts.
func SRIOVGetHostDevicesInUse(s *state.State) (map[string]struct{}, error) {
	sriovReservedDevicesMutex.Lock()
	defer sriovReservedDevicesMutex.Unlock()

	var err error
	var localNode string
	var projectNetworks map[string]map[int64]api.Network

	err = s.Cluster.Transaction(func(tx *db.ClusterTx) error {
		localNode, err = tx.GetLocalNodeName()
		if err != nil {
			return errors.Wrapf(err, "Failed to get local node name")
		}

		// Get all managed networks across all projects.
		projectNetworks, err = tx.GetCreatedNetworks()
		if err != nil {
			return errors.Wrapf(err, "Failed to load all networks")
		}

		return err
	})
	if err != nil {
		return nil, err
	}

	filter := db.InstanceFilter{
		Node: &localNode,
	}

	reservedDevices := map[string]struct{}{}

	// Check if any instances are using the VF device.
	err = s.Cluster.InstanceList(&filter, func(dbInst db.Instance, p db.Project, profiles []api.Profile) error {
		// Expand configs so we take into account profile devices.
		dbInst.Config = db.ExpandInstanceConfig(dbInst.Config, profiles)
		dbInst.Devices = db.ExpandInstanceDevices(deviceConfig.NewDevices(dbInst.Devices), profiles).CloneNative()

		for devName, devConfig := range dbInst.Devices {
			// If device references a parent host interface name, mark that as reserved.
			parent := devConfig["parent"]
			if parent != "" {
				reservedDevices[parent] = struct{}{}
			}

			// If device references a volatile host interface name, mark that as reserved.
			hostName := dbInst.Config[fmt.Sprintf("volatile.%s.host_name", devName)]
			if hostName != "" {
				reservedDevices[hostName] = struct{}{}
			}
		}

		return nil
	})
	if err != nil {
		return nil, err
	}

	// Check if any networks are using the VF device.
	for _, networks := range projectNetworks {
		for _, ni := range networks {
			// If network references a parent host interface name, mark that as reserved.
			parent := ni.Config["parent"]
			if parent != "" {
				reservedDevices[parent] = struct{}{}
			}
		}
	}

	return reservedDevices, nil
}

// SRIOVFindFreeVirtualFunction looks on the specified parent device for an unused virtual function.
// Returns the name of the interface and virtual function index ID if found, error if not.
func SRIOVFindFreeVirtualFunction(s *state.State, parentDev string) (string, int, error) {
	sriovFindFreeVirtualFunctionMutex.Lock()
	defer sriovFindFreeVirtualFunctionMutex.Unlock()

	reservedDevices, err := SRIOVGetHostDevicesInUse(s)
	if err != nil {
		return "", -1, errors.Wrapf(err, "Failed getting in use device list")
	}

	sriovNumVFsFile := fmt.Sprintf("/sys/class/net/%s/device/sriov_numvfs", parentDev)
	sriovTotalVFsFile := fmt.Sprintf("/sys/class/net/%s/device/sriov_totalvfs", parentDev)

	// Verify that this is indeed a SR-IOV enabled device.
	if !shared.PathExists(sriovNumVFsFile) {
		return "", -1, fmt.Errorf("Parent device %q doesn't support SR-IOV", parentDev)
	}

	// Get parent dev_port and dev_id values.
	pfDevPort, err := ioutil.ReadFile(fmt.Sprintf("/sys/class/net/%s/dev_port", parentDev))
	if err != nil {
		return "", -1, err
	}

	pfDevID, err := ioutil.ReadFile(fmt.Sprintf("/sys/class/net/%s/dev_id", parentDev))
	if err != nil {
		return "", -1, err
	}

	// Get number of currently enabled VFs.
	sriovNumVFsBuf, err := ioutil.ReadFile(sriovNumVFsFile)
	if err != nil {
		return "", -1, err
	}

	sriovNumVFs, err := strconv.Atoi(strings.TrimSpace(string(sriovNumVFsBuf)))
	if err != nil {
		return "", -1, err
	}

	// Get number of possible VFs.
	sriovTotalVFsBuf, err := ioutil.ReadFile(sriovTotalVFsFile)
	if err != nil {
		return "", -1, err
	}

	sriovTotalVFs, err := strconv.Atoi(strings.TrimSpace(string(sriovTotalVFsBuf)))
	if err != nil {
		return "", -1, err
	}

	// Ensure parent is up (needed for Intel at least).
	link := &ip.Link{Name: parentDev}
	err = link.SetUp()
	if err != nil {
		return "", -1, err
	}

	// Check if any free VFs are already enabled.
	vfID, nicName, err := sriovGetFreeVFInterface(reservedDevices, parentDev, sriovNumVFs, 0, pfDevID, pfDevPort)
	if err != nil {
		return "", -1, err
	}

	// Found a free VF.
	if nicName != "" {
		return nicName, vfID, nil
	} else if sriovNumVFs < sriovTotalVFs {
		logger.Debugf("Attempting to grow available VFs from %d to %d on device %q", sriovNumVFs, sriovTotalVFs, parentDev)

		// Bump the number of VFs to the maximum if not there yet.
		err = ioutil.WriteFile(sriovNumVFsFile, []byte(fmt.Sprintf("%d", sriovTotalVFs)), 0644)
		if err != nil {
			return "", -1, errors.Wrapf(err, "Failed growing available VFs from %d to %d on device %q", sriovNumVFs, sriovTotalVFs, parentDev)
		}

		time.Sleep(time.Second) // Allow time for new VFs to appear.

		// Use next free VF index starting from the first newly created VF.
		vfID, nicName, err = sriovGetFreeVFInterface(reservedDevices, parentDev, sriovTotalVFs, sriovNumVFs, pfDevID, pfDevPort)
		if err != nil {
			return "", -1, err
		}

		// Found a free VF.
		if nicName != "" {
			return nicName, vfID, nil
		}
	}

	return "", -1, fmt.Errorf("All virtual functions on parent device %q are already in use", parentDev)
}

// sriovGetFreeVFInterface checks the system for a free VF interface that belongs to the same device and port as
// the parent device starting from the startVFID to the vfCount-1. Returns VF ID and VF interface name if found or
// -1 and empty string if no free interface found. A free interface is one that is bound on the host, not in the
// reservedDevices map, is down and has no global IPs defined on it.
func sriovGetFreeVFInterface(reservedDevices map[string]struct{}, parentDev string, vfCount int, startVFID int, pfDevID []byte, pfDevPort []byte) (int, string, error) {
	for vfID := startVFID; vfID < vfCount; vfID++ {
		vfListPath := fmt.Sprintf("/sys/class/net/%s/device/virtfn%d/net", parentDev, vfID)

		if !shared.PathExists(vfListPath) {
			continue // The vfListPath won't exist if the VF has been unbound and used with a VM.
		}

		ents, err := ioutil.ReadDir(vfListPath)
		if err != nil {
			return -1, "", errors.Wrapf(err, "Failed reading VF interface directory %q", vfListPath)
		}

		for _, ent := range ents {
			// We expect the entry to be a directory for the VF's interface name.
			if !ent.IsDir() {
				continue
			}

			nicName := ent.Name()

			// We can't use this VF interface as it is reserved by another device.
			_, exists := reservedDevices[nicName]
			if exists {
				continue
			}

			// Get VF dev_port and dev_id values.
			vfDevPort, err := ioutil.ReadFile(fmt.Sprintf("%s/%s/dev_port", vfListPath, nicName))
			if err != nil {
				return -1, "", err
			}

			vfDevID, err := ioutil.ReadFile(fmt.Sprintf("%s/%s/dev_id", vfListPath, nicName))
			if err != nil {
				return -1, "", err
			}

			// Skip VFs if they do not relate to the same device and port as the parent PF.
			// Some card vendors change the device ID for each port.
			if bytes.Compare(pfDevPort, vfDevPort) != 0 || bytes.Compare(pfDevID, vfDevID) != 0 {
				continue
			}

			addresses, isUp, err := InterfaceStatus(nicName)
			if err != nil {
				return -1, "", err
			}

			// Ignore if interface is up or if interface has unicast IP addresses (may be in use by
			// another application already).
			if isUp || len(addresses) > 0 {
				continue
			}

			// Found a free VF.
			return vfID, nicName, err
		}
	}

	return -1, "", nil
}

// SRIOVGetVFDevicePCISlot returns the PCI slot name for a network virtual function device.
func SRIOVGetVFDevicePCISlot(parentDev string, vfID string) (pci.Device, error) {
	ueventFile := fmt.Sprintf("/sys/class/net/%s/device/virtfn%s/uevent", parentDev, vfID)
	pciDev, err := pci.ParseUeventFile(ueventFile)
	if err != nil {
		return pciDev, err
	}

	return pciDev, nil
}
<commit_msg>lxd/network/network/utils/sriov: Remove use of lock from SRIOVFindFreeVirtualFunction<commit_after>package network

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/pkg/errors"

	"github.com/lxc/lxd/lxd/db"
	deviceConfig "github.com/lxc/lxd/lxd/device/config"
	"github.com/lxc/lxd/lxd/device/pci"
	"github.com/lxc/lxd/lxd/ip"
	"github.com/lxc/lxd/lxd/state"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
	"github.com/lxc/lxd/shared/logger"
)

// sriovReservedDevicesMutex used to coordinate access for checking reserved devices.
var sriovReservedDevicesMutex sync.Mutex

// SRIOVVirtualFunctionMutex used to coordinate access for finding and claiming free virtual functions.
var SRIOVVirtualFunctionMutex sync.Mutex

// SRIOVGetHostDevicesInUse returns a map of host device names that have been used by devices in other instances
// and networks on the local node. Used when selecting physical and SR-IOV VF devices to avoid conflicts.
func SRIOVGetHostDevicesInUse(s *state.State) (map[string]struct{}, error) {
	sriovReservedDevicesMutex.Lock()
	defer sriovReservedDevicesMutex.Unlock()

	var err error
	var localNode string
	var projectNetworks map[string]map[int64]api.Network

	err = s.Cluster.Transaction(func(tx *db.ClusterTx) error {
		localNode, err = tx.GetLocalNodeName()
		if err != nil {
			return errors.Wrapf(err, "Failed to get local node name")
		}

		// Get all managed networks across all projects.
		projectNetworks, err = tx.GetCreatedNetworks()
		if err != nil {
			return errors.Wrapf(err, "Failed to load all networks")
		}

		return err
	})
	if err != nil {
		return nil, err
	}

	filter := db.InstanceFilter{
		Node: &localNode,
	}

	reservedDevices := map[string]struct{}{}

	// Check if any instances are using the VF device.
	err = s.Cluster.InstanceList(&filter, func(dbInst db.Instance, p db.Project, profiles []api.Profile) error {
		// Expand configs so we take into account profile devices.
		dbInst.Config = db.ExpandInstanceConfig(dbInst.Config, profiles)
		dbInst.Devices = db.ExpandInstanceDevices(deviceConfig.NewDevices(dbInst.Devices), profiles).CloneNative()

		for devName, devConfig := range dbInst.Devices {
			// If device references a parent host interface name, mark that as reserved.
			parent := devConfig["parent"]
			if parent != "" {
				reservedDevices[parent] = struct{}{}
			}

			// If device references a volatile host interface name, mark that as reserved.
			hostName := dbInst.Config[fmt.Sprintf("volatile.%s.host_name", devName)]
			if hostName != "" {
				reservedDevices[hostName] = struct{}{}
			}
		}

		return nil
	})
	if err != nil {
		return nil, err
	}

	// Check if any networks are using the VF device.
	for _, networks := range projectNetworks {
		for _, ni := range networks {
			// If network references a parent host interface name, mark that as reserved.
			parent := ni.Config["parent"]
			if parent != "" {
				reservedDevices[parent] = struct{}{}
			}
		}
	}

	return reservedDevices, nil
}

// SRIOVFindFreeVirtualFunction looks on the specified parent device for an unused virtual function.
// Returns the name of the interface and virtual function index ID if found, error if not.
func SRIOVFindFreeVirtualFunction(s *state.State, parentDev string) (string, int, error) {
	reservedDevices, err := SRIOVGetHostDevicesInUse(s)
	if err != nil {
		return "", -1, errors.Wrapf(err, "Failed getting in use device list")
	}

	sriovNumVFsFile := fmt.Sprintf("/sys/class/net/%s/device/sriov_numvfs", parentDev)
	sriovTotalVFsFile := fmt.Sprintf("/sys/class/net/%s/device/sriov_totalvfs", parentDev)

	// Verify that this is indeed a SR-IOV enabled device.
	if !shared.PathExists(sriovNumVFsFile) {
		return "", -1, fmt.Errorf("Parent device %q doesn't support SR-IOV", parentDev)
	}

	// Get parent dev_port and dev_id values.
	pfDevPort, err := ioutil.ReadFile(fmt.Sprintf("/sys/class/net/%s/dev_port", parentDev))
	if err != nil {
		return "", -1, err
	}

	pfDevID, err := ioutil.ReadFile(fmt.Sprintf("/sys/class/net/%s/dev_id", parentDev))
	if err != nil {
		return "", -1, err
	}

	// Get number of currently enabled VFs.
	sriovNumVFsBuf, err := ioutil.ReadFile(sriovNumVFsFile)
	if err != nil {
		return "", -1, err
	}

	sriovNumVFs, err := strconv.Atoi(strings.TrimSpace(string(sriovNumVFsBuf)))
	if err != nil {
		return "", -1, err
	}

	// Get number of possible VFs.
	sriovTotalVFsBuf, err := ioutil.ReadFile(sriovTotalVFsFile)
	if err != nil {
		return "", -1, err
	}

	sriovTotalVFs, err := strconv.Atoi(strings.TrimSpace(string(sriovTotalVFsBuf)))
	if err != nil {
		return "", -1, err
	}

	// Ensure parent is up (needed for Intel at least).
	link := &ip.Link{Name: parentDev}
	err = link.SetUp()
	if err != nil {
		return "", -1, err
	}

	// Check if any free VFs are already enabled.
	vfID, nicName, err := sriovGetFreeVFInterface(reservedDevices, parentDev, sriovNumVFs, 0, pfDevID, pfDevPort)
	if err != nil {
		return "", -1, err
	}

	// Found a free VF.
	if nicName != "" {
		return nicName, vfID, nil
	} else if sriovNumVFs < sriovTotalVFs {
		logger.Debugf("Attempting to grow available VFs from %d to %d on device %q", sriovNumVFs, sriovTotalVFs, parentDev)

		// Bump the number of VFs to the maximum if not there yet.
		err = ioutil.WriteFile(sriovNumVFsFile, []byte(fmt.Sprintf("%d", sriovTotalVFs)), 0644)
		if err != nil {
			return "", -1, errors.Wrapf(err, "Failed growing available VFs from %d to %d on device %q", sriovNumVFs, sriovTotalVFs, parentDev)
		}

		time.Sleep(time.Second) // Allow time for new VFs to appear.

		// Use next free VF index starting from the first newly created VF.
		vfID, nicName, err = sriovGetFreeVFInterface(reservedDevices, parentDev, sriovTotalVFs, sriovNumVFs, pfDevID, pfDevPort)
		if err != nil {
			return "", -1, err
		}

		// Found a free VF.
		if nicName != "" {
			return nicName, vfID, nil
		}
	}

	return "", -1, fmt.Errorf("All virtual functions on parent device %q are already in use", parentDev)
}

// sriovGetFreeVFInterface checks the system for a free VF interface that belongs to the same device and port as
// the parent device starting from the startVFID to the vfCount-1. Returns VF ID and VF interface name if found or
// -1 and empty string if no free interface found. A free interface is one that is bound on the host, not in the
// reservedDevices map, is down and has no global IPs defined on it.
func sriovGetFreeVFInterface(reservedDevices map[string]struct{}, parentDev string, vfCount int, startVFID int, pfDevID []byte, pfDevPort []byte) (int, string, error) {
	for vfID := startVFID; vfID < vfCount; vfID++ {
		vfListPath := fmt.Sprintf("/sys/class/net/%s/device/virtfn%d/net", parentDev, vfID)

		if !shared.PathExists(vfListPath) {
			continue // The vfListPath won't exist if the VF has been unbound and used with a VM.
		}

		ents, err := ioutil.ReadDir(vfListPath)
		if err != nil {
			return -1, "", errors.Wrapf(err, "Failed reading VF interface directory %q", vfListPath)
		}

		for _, ent := range ents {
			// We expect the entry to be a directory for the VF's interface name.
			if !ent.IsDir() {
				continue
			}

			nicName := ent.Name()

			// We can't use this VF interface as it is reserved by another device.
			_, exists := reservedDevices[nicName]
			if exists {
				continue
			}

			// Get VF dev_port and dev_id values.
			vfDevPort, err := ioutil.ReadFile(fmt.Sprintf("%s/%s/dev_port", vfListPath, nicName))
			if err != nil {
				return -1, "", err
			}

			vfDevID, err := ioutil.ReadFile(fmt.Sprintf("%s/%s/dev_id", vfListPath, nicName))
			if err != nil {
				return -1, "", err
			}

			// Skip VFs if they do not relate to the same device and port as the parent PF.
			// Some card vendors change the device ID for each port.
			if bytes.Compare(pfDevPort, vfDevPort) != 0 || bytes.Compare(pfDevID, vfDevID) != 0 {
				continue
			}

			addresses, isUp, err := InterfaceStatus(nicName)
			if err != nil {
				return -1, "", err
			}

			// Ignore if interface is up or if interface has unicast IP addresses (may be in use by
			// another application already).
			if isUp || len(addresses) > 0 {
				continue
			}

			// Found a free VF.
			return vfID, nicName, err
		}
	}

	return -1, "", nil
}

// SRIOVGetVFDevicePCISlot returns the PCI slot name for a network virtual function device.
func SRIOVGetVFDevicePCISlot(parentDev string, vfID string) (pci.Device, error) {
	ueventFile := fmt.Sprintf("/sys/class/net/%s/device/virtfn%s/uevent", parentDev, vfID)
	pciDev, err := pci.ParseUeventFile(ueventFile)
	if err != nil {
		return pciDev, err
	}

	return pciDev, nil
}
<|endoftext|>


<commit_before>package lxd

import (
	"fmt"
	"strings"
	"testing"

	"github.com/dustinkirkland/golang-petname"

	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"

	"github.com/lxc/lxd/shared"
)

func TestAccContainer_basic(t *testing.T) {
	var container shared.ContainerInfo
	containerName := strings.ToLower(petname.Generate(2, "-"))

	resource.Test(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccContainer_basic(containerName),
				Check: resource.ComposeTestCheckFunc(
					testAccContainerRunning(t, "lxd_container.container1", &container),
					resource.TestCheckResourceAttr("lxd_container.container1", "name", containerName),
				),
			},
		},
	})
}

func TestAccContainer_config(t *testing.T) {
	var container shared.ContainerInfo
	containerName := strings.ToLower(petname.Generate(2, "-"))

	resource.Test(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccContainer_config(containerName),
				Check: resource.ComposeTestCheckFunc(
					resource.TestCheckResourceAttr("lxd_container.container1", "name", containerName),
					resource.TestCheckResourceAttr("lxd_container.container1", "config.limits.cpu", "2"),
					testAccContainerRunning(t, "lxd_container.container1", &container),
					testAccContainerConfig(&container, "limits.cpu", "2"),
				),
			},
		},
	})
}

func TestAccContainer_update(t *testing.T) {
	var container shared.ContainerInfo
	containerName := strings.ToLower(petname.Generate(2, "-"))

	resource.Test(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccContainer_update_1(containerName),
				Check: resource.ComposeTestCheckFunc(
					resource.TestCheckResourceAttr("lxd_container.container1", "name", containerName),
					resource.TestCheckResourceAttr("lxd_container.container1", "profiles.0", "default"),
					testAccContainerRunning(t, "lxd_container.container1", &container),
				),
			},
			resource.TestStep{
				Config: testAccContainer_update_2(containerName),
				Check: resource.ComposeTestCheckFunc(
					resource.TestCheckResourceAttr("lxd_container.container1", "name", containerName),
					resource.TestCheckResourceAttr("lxd_container.container1", "profiles.1", "docker"),
					testAccContainerRunning(t, "lxd_container.container1", &container),
				),
			},
		},
	})
}

func testAccContainerRunning(t *testing.T, n string, container *shared.ContainerInfo) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[n]
		if !ok {
			return fmt.Errorf("Not found: %s", n)
		}

		if rs.Primary.ID == "" {
			return fmt.Errorf("No ID is set")
		}

		client := testAccProvider.Meta().(*LxdProvider).Client
		ct, err := client.ContainerInfo(rs.Primary.ID)
		if err != nil {
			return err
		}

		if ct != nil {
			*container = *ct
			return nil
		}

		return fmt.Errorf("Container not found: %s", rs.Primary.ID)
	}
}

func testAccContainerConfig(container *shared.ContainerInfo, k, v string) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		if container.Config == nil {
			return fmt.Errorf("No config")
		}

		for key, value := range container.Config {
			if k != key {
				continue
			}

			if v == value {
				return nil
			}

			return fmt.Errorf("Bad value for %s: %s", k, value)
		}

		return fmt.Errorf("Config not found: %s", k)
	}
}

func testAccContainer_basic(name string) string {
	return fmt.Sprintf(`resource "lxd_container" "container1" {
  name = "%s"
  image = "ubuntu"
  profiles = ["default"]
}`, name)
}

func testAccContainer_config(name string) string {
	return fmt.Sprintf(`resource "lxd_container" "container1" {
  name = "%s"
  image = "ubuntu"
  profiles = ["default"]
  config {
    limits.cpu = 2
  }
}`, name)
}

func testAccContainer_update_1(name string) string {
	return fmt.Sprintf(`resource "lxd_container" "container1" {
	name = "%s"
	image = "ubuntu"
	profiles = ["default"]
}`, name)
}

func testAccContainer_update_2(name string) string {
	return fmt.Sprintf(`resource "lxd_container" "container1" {
	name = "%s"
	image = "ubuntu"
	profiles = ["default", "docker"]
}`, name)
}
<commit_msg>Update Profile acceptance tests<commit_after>package lxd

import (
	"fmt"
	"strings"
	"testing"

	"github.com/dustinkirkland/golang-petname"

	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"

	"github.com/lxc/lxd/shared"
)

func TestAccContainer_basic(t *testing.T) {
	var container shared.ContainerInfo
	containerName := strings.ToLower(petname.Generate(2, "-"))

	resource.Test(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccContainer_basic(containerName),
				Check: resource.ComposeTestCheckFunc(
					testAccContainerRunning(t, "lxd_container.container1", &container),
					resource.TestCheckResourceAttr("lxd_container.container1", "name", containerName),
				),
			},
		},
	})
}

func TestAccContainer_config(t *testing.T) {
	var container shared.ContainerInfo
	containerName := strings.ToLower(petname.Generate(2, "-"))

	resource.Test(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccContainer_config(containerName),
				Check: resource.ComposeTestCheckFunc(
					resource.TestCheckResourceAttr("lxd_container.container1", "name", containerName),
					resource.TestCheckResourceAttr("lxd_container.container1", "config.limits.cpu", "2"),
					testAccContainerRunning(t, "lxd_container.container1", &container),
					testAccContainerConfig(&container, "limits.cpu", "2"),
				),
			},
		},
	})
}

func TestAccContainer_profile(t *testing.T) {
	var container shared.ContainerInfo
	containerName := strings.ToLower(petname.Generate(2, "-"))

	resource.Test(t, resource.TestCase{
		PreCheck:  func() { testAccPreCheck(t) },
		Providers: testAccProviders,
		Steps: []resource.TestStep{
			resource.TestStep{
				Config: testAccContainer_profile_1(containerName),
				Check: resource.ComposeTestCheckFunc(
					resource.TestCheckResourceAttr("lxd_container.container1", "name", containerName),
					resource.TestCheckResourceAttr("lxd_container.container1", "profiles.0", "default"),
					testAccContainerRunning(t, "lxd_container.container1", &container),
					testAccContainerProfile(&container, "default"),
				),
			},
			resource.TestStep{
				Config: testAccContainer_profile_2(containerName),
				Check: resource.ComposeTestCheckFunc(
					resource.TestCheckResourceAttr("lxd_container.container1", "name", containerName),
					resource.TestCheckResourceAttr("lxd_container.container1", "profiles.1", "docker"),
					testAccContainerRunning(t, "lxd_container.container1", &container),
					testAccContainerProfile(&container, "docker"),
				),
			},
		},
	})
}

func testAccContainerRunning(t *testing.T, n string, container *shared.ContainerInfo) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[n]
		if !ok {
			return fmt.Errorf("Not found: %s", n)
		}

		if rs.Primary.ID == "" {
			return fmt.Errorf("No ID is set")
		}

		client := testAccProvider.Meta().(*LxdProvider).Client
		ct, err := client.ContainerInfo(rs.Primary.ID)
		if err != nil {
			return err
		}

		if ct != nil {
			*container = *ct
			return nil
		}

		return fmt.Errorf("Container not found: %s", rs.Primary.ID)
	}
}

func testAccContainerConfig(container *shared.ContainerInfo, k, v string) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		if container.Config == nil {
			return fmt.Errorf("No config")
		}

		for key, value := range container.Config {
			if k != key {
				continue
			}

			if v == value {
				return nil
			}

			return fmt.Errorf("Bad value for %s: %s", k, value)
		}

		return fmt.Errorf("Config not found: %s", k)
	}
}

func testAccContainerProfile(container *shared.ContainerInfo, profile string) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		if container.Profiles == nil {
			return fmt.Errorf("No config")
		}

		for _, v := range container.Profiles {
			if v == profile {
				return nil
			}
		}

		return fmt.Errorf("Profile not found: %s", profile)
	}
}

func testAccContainer_basic(name string) string {
	return fmt.Sprintf(`resource "lxd_container" "container1" {
  name = "%s"
  image = "ubuntu"
  profiles = ["default"]
}`, name)
}

func testAccContainer_config(name string) string {
	return fmt.Sprintf(`resource "lxd_container" "container1" {
  name = "%s"
  image = "ubuntu"
  profiles = ["default"]
  config {
    limits.cpu = 2
  }
}`, name)
}

func testAccContainer_profile_1(name string) string {
	return fmt.Sprintf(`resource "lxd_container" "container1" {
	name = "%s"
	image = "ubuntu"
	profiles = ["default"]
}`, name)
}

func testAccContainer_profile_2(name string) string {
	return fmt.Sprintf(`resource "lxd_container" "container1" {
	name = "%s"
	image = "ubuntu"
	profiles = ["default", "docker"]
}`, name)
}
<|endoftext|>