<commit_before>// ----------------------------------------------------------------------------
//
//     ***     AUTO GENERATED CODE    ***    AUTO GENERATED CODE     ***
//
// ----------------------------------------------------------------------------
//
//     This file is automatically generated by Magic Modules and manual
//     changes will be clobbered when the file is regenerated.
//
//     Please read more about how to change this file in
//     .github/CONTRIBUTING.md.
//
// ----------------------------------------------------------------------------

package google

import (
	"fmt"
	"reflect"
)

func GetComputeInterconnectAttachmentCaiObject(d TerraformResourceData, config *Config) (Asset, error) {
	name, err := assetName(d, config, "//compute.googleapis.com/projects/{{project}}/regions/{{region}}/interconnectAttachments/{{name}}")
	if err != nil {
		return Asset{}, err
	}
	if obj, err := GetComputeInterconnectAttachmentApiObject(d, config); err == nil {
		return Asset{
			Name: name,
			Type: "compute.googleapis.com/InterconnectAttachment",
			Resource: &AssetResource{
				Version:              "v1",
				DiscoveryDocumentURI: "https://www.googleapis.com/discovery/v1/apis/compute/v1/rest",
				DiscoveryName:        "InterconnectAttachment",
				Data:                 obj,
			},
		}, nil
	} else {
		return Asset{}, err
	}
}

func GetComputeInterconnectAttachmentApiObject(d TerraformResourceData, config *Config) (map[string]interface{}, error) {
	obj := make(map[string]interface{})
	interconnectProp, err := expandComputeInterconnectAttachmentInterconnect(d.Get("interconnect"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("interconnect"); !isEmptyValue(reflect.ValueOf(interconnectProp)) && (ok || !reflect.DeepEqual(v, interconnectProp)) {
		obj["interconnect"] = interconnectProp
	}
	descriptionProp, err := expandComputeInterconnectAttachmentDescription(d.Get("description"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("description"); !isEmptyValue(reflect.ValueOf(descriptionProp)) && (ok || !reflect.DeepEqual(v, descriptionProp)) {
		obj["description"] = descriptionProp
	}
	edgeAvailabilityDomainProp, err := expandComputeInterconnectAttachmentEdgeAvailabilityDomain(d.Get("edge_availability_domain"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("edge_availability_domain"); !isEmptyValue(reflect.ValueOf(edgeAvailabilityDomainProp)) && (ok || !reflect.DeepEqual(v, edgeAvailabilityDomainProp)) {
		obj["edgeAvailabilityDomain"] = edgeAvailabilityDomainProp
	}
	typeProp, err := expandComputeInterconnectAttachmentType(d.Get("type"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("type"); !isEmptyValue(reflect.ValueOf(typeProp)) && (ok || !reflect.DeepEqual(v, typeProp)) {
		obj["type"] = typeProp
	}
	routerProp, err := expandComputeInterconnectAttachmentRouter(d.Get("router"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("router"); !isEmptyValue(reflect.ValueOf(routerProp)) && (ok || !reflect.DeepEqual(v, routerProp)) {
		obj["router"] = routerProp
	}
	nameProp, err := expandComputeInterconnectAttachmentName(d.Get("name"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("name"); !isEmptyValue(reflect.ValueOf(nameProp)) && (ok || !reflect.DeepEqual(v, nameProp)) {
		obj["name"] = nameProp
	}
	candidateSubnetsProp, err := expandComputeInterconnectAttachmentCandidateSubnets(d.Get("candidate_subnets"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("candidate_subnets"); !isEmptyValue(reflect.ValueOf(candidateSubnetsProp)) && (ok || !reflect.DeepEqual(v, candidateSubnetsProp)) {
		obj["candidateSubnets"] = candidateSubnetsProp
	}
	vlanTag8021qProp, err := expandComputeInterconnectAttachmentVlanTag8021q(d.Get("vlan_tag8021q"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("vlan_tag8021q"); !isEmptyValue(reflect.ValueOf(vlanTag8021qProp)) && (ok || !reflect.DeepEqual(v, vlanTag8021qProp)) {
		obj["vlanTag8021q"] = vlanTag8021qProp
	}
	regionProp, err := expandComputeInterconnectAttachmentRegion(d.Get("region"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("region"); !isEmptyValue(reflect.ValueOf(regionProp)) && (ok || !reflect.DeepEqual(v, regionProp)) {
		obj["region"] = regionProp
	}

	return obj, nil
}

func expandComputeInterconnectAttachmentInterconnect(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandComputeInterconnectAttachmentDescription(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandComputeInterconnectAttachmentEdgeAvailabilityDomain(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandComputeInterconnectAttachmentType(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandComputeInterconnectAttachmentRouter(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	f, err := parseRegionalFieldValue("routers", v.(string), "project", "region", "zone", d, config, true)
	if err != nil {
		return nil, fmt.Errorf("Invalid value for router: %s", err)
	}
	return f.RelativeLink(), nil
}

func expandComputeInterconnectAttachmentName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandComputeInterconnectAttachmentCandidateSubnets(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandComputeInterconnectAttachmentVlanTag8021q(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandComputeInterconnectAttachmentRegion(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	f, err := parseGlobalFieldValue("regions", v.(string), "project", d, config, true)
	if err != nil {
		return nil, fmt.Errorf("Invalid value for region: %s", err)
	}
	return f.RelativeLink(), nil
}
<commit_msg>Bandwidth addition to interconnect (#150)<commit_after>// ----------------------------------------------------------------------------
//
//     ***     AUTO GENERATED CODE    ***    AUTO GENERATED CODE     ***
//
// ----------------------------------------------------------------------------
//
//     This file is automatically generated by Magic Modules and manual
//     changes will be clobbered when the file is regenerated.
//
//     Please read more about how to change this file in
//     .github/CONTRIBUTING.md.
//
// ----------------------------------------------------------------------------

package google

import (
	"fmt"
	"reflect"
)

func GetComputeInterconnectAttachmentCaiObject(d TerraformResourceData, config *Config) (Asset, error) {
	name, err := assetName(d, config, "//compute.googleapis.com/projects/{{project}}/regions/{{region}}/interconnectAttachments/{{name}}")
	if err != nil {
		return Asset{}, err
	}
	if obj, err := GetComputeInterconnectAttachmentApiObject(d, config); err == nil {
		return Asset{
			Name: name,
			Type: "compute.googleapis.com/InterconnectAttachment",
			Resource: &AssetResource{
				Version:              "v1",
				DiscoveryDocumentURI: "https://www.googleapis.com/discovery/v1/apis/compute/v1/rest",
				DiscoveryName:        "InterconnectAttachment",
				Data:                 obj,
			},
		}, nil
	} else {
		return Asset{}, err
	}
}

func GetComputeInterconnectAttachmentApiObject(d TerraformResourceData, config *Config) (map[string]interface{}, error) {
	obj := make(map[string]interface{})
	interconnectProp, err := expandComputeInterconnectAttachmentInterconnect(d.Get("interconnect"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("interconnect"); !isEmptyValue(reflect.ValueOf(interconnectProp)) && (ok || !reflect.DeepEqual(v, interconnectProp)) {
		obj["interconnect"] = interconnectProp
	}
	descriptionProp, err := expandComputeInterconnectAttachmentDescription(d.Get("description"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("description"); !isEmptyValue(reflect.ValueOf(descriptionProp)) && (ok || !reflect.DeepEqual(v, descriptionProp)) {
		obj["description"] = descriptionProp
	}
	bandwidthProp, err := expandComputeInterconnectAttachmentBandwidth(d.Get("bandwidth"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("bandwidth"); !isEmptyValue(reflect.ValueOf(bandwidthProp)) && (ok || !reflect.DeepEqual(v, bandwidthProp)) {
		obj["bandwidth"] = bandwidthProp
	}
	edgeAvailabilityDomainProp, err := expandComputeInterconnectAttachmentEdgeAvailabilityDomain(d.Get("edge_availability_domain"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("edge_availability_domain"); !isEmptyValue(reflect.ValueOf(edgeAvailabilityDomainProp)) && (ok || !reflect.DeepEqual(v, edgeAvailabilityDomainProp)) {
		obj["edgeAvailabilityDomain"] = edgeAvailabilityDomainProp
	}
	typeProp, err := expandComputeInterconnectAttachmentType(d.Get("type"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("type"); !isEmptyValue(reflect.ValueOf(typeProp)) && (ok || !reflect.DeepEqual(v, typeProp)) {
		obj["type"] = typeProp
	}
	routerProp, err := expandComputeInterconnectAttachmentRouter(d.Get("router"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("router"); !isEmptyValue(reflect.ValueOf(routerProp)) && (ok || !reflect.DeepEqual(v, routerProp)) {
		obj["router"] = routerProp
	}
	nameProp, err := expandComputeInterconnectAttachmentName(d.Get("name"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("name"); !isEmptyValue(reflect.ValueOf(nameProp)) && (ok || !reflect.DeepEqual(v, nameProp)) {
		obj["name"] = nameProp
	}
	candidateSubnetsProp, err := expandComputeInterconnectAttachmentCandidateSubnets(d.Get("candidate_subnets"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("candidate_subnets"); !isEmptyValue(reflect.ValueOf(candidateSubnetsProp)) && (ok || !reflect.DeepEqual(v, candidateSubnetsProp)) {
		obj["candidateSubnets"] = candidateSubnetsProp
	}
	vlanTag8021qProp, err := expandComputeInterconnectAttachmentVlanTag8021q(d.Get("vlan_tag8021q"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("vlan_tag8021q"); !isEmptyValue(reflect.ValueOf(vlanTag8021qProp)) && (ok || !reflect.DeepEqual(v, vlanTag8021qProp)) {
		obj["vlanTag8021q"] = vlanTag8021qProp
	}
	regionProp, err := expandComputeInterconnectAttachmentRegion(d.Get("region"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("region"); !isEmptyValue(reflect.ValueOf(regionProp)) && (ok || !reflect.DeepEqual(v, regionProp)) {
		obj["region"] = regionProp
	}

	return obj, nil
}

func expandComputeInterconnectAttachmentInterconnect(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandComputeInterconnectAttachmentDescription(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandComputeInterconnectAttachmentBandwidth(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandComputeInterconnectAttachmentEdgeAvailabilityDomain(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandComputeInterconnectAttachmentType(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandComputeInterconnectAttachmentRouter(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	f, err := parseRegionalFieldValue("routers", v.(string), "project", "region", "zone", d, config, true)
	if err != nil {
		return nil, fmt.Errorf("Invalid value for router: %s", err)
	}
	return f.RelativeLink(), nil
}

func expandComputeInterconnectAttachmentName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandComputeInterconnectAttachmentCandidateSubnets(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandComputeInterconnectAttachmentVlanTag8021q(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}

func expandComputeInterconnectAttachmentRegion(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	f, err := parseGlobalFieldValue("regions", v.(string), "project", d, config, true)
	if err != nil {
		return nil, fmt.Errorf("Invalid value for region: %s", err)
	}
	return f.RelativeLink(), nil
}
<|endoftext|>


<commit_before>package backends

import (
	"log"
	"time"
)

const (
	AuthorizationExpiration = 900
	AccessExpiration        = 86400
)

// 清理过期的数据
func Cleanup() error {
	now := time.Now()
	return withDbQuery(func(db dber) (err error) {
		_, err = db.Exec("DELETE FROM oauth_authorization_code WHERE created < $1", now.Add(-time.Second*AuthorizationExpiration))
		if err != nil {
			log.Printf("clean authorize ERR %s", err)
			return
		}
		_, err = db.Exec("DELETE FROM oauth_access_token WHERE created < $1", now.Add(-time.Second*AccessExpiration))
		if err != nil {
			log.Printf("clean access ERR %s", err)
			return
		}

		_, err = db.Exec("DELETE FROM http_sessions WHERE expires_on < now()")
		if err != nil {
			log.Printf("clean sessions ERR %s", err)
			return
		}
		debug("Cleanup done at %s", now)
		return
	})
}

// a+x>b
<commit_msg>more details of log<commit_after>package backends

import (
	"database/sql"
	"log"
	"time"
)

const (
	AuthorizationExpiration = 900
	AccessExpiration        = 86400
)

// 清理过期的数据
func Cleanup() error {
	now := time.Now()
	return withDbQuery(func(db dber) (err error) {
		var (
			r1, r2, r3 sql.Result
			c1, c2, c3 int64
		)
		r1, err = db.Exec("DELETE FROM oauth_authorization_code WHERE created < $1", now.Add(-time.Second*AuthorizationExpiration))
		if err != nil {
			log.Printf("clean authorize ERR %s", err)
			return
		}
		c1, _ = r1.RowsAffected()
		r2, err = db.Exec("DELETE FROM oauth_access_token WHERE created < $1", now.Add(-time.Second*AccessExpiration))
		if err != nil {
			log.Printf("clean access ERR %s", err)
			return
		}
		c2, _ = r2.RowsAffected()
		r3, err = db.Exec("DELETE FROM http_sessions WHERE expires_on < now()")
		if err != nil {
			log.Printf("clean sessions ERR %s", err)
			return
		}
		c3, _ = r3.RowsAffected()
		debug("Cleanup done at %s: %d, %d, %d", now, c1, c2, c3)
		return
	})
}

// a+x>b
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

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 chartutil

import (
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"strings"

	"github.com/ghodss/yaml"
	"github.com/golang/protobuf/ptypes/timestamp"
	"k8s.io/helm/pkg/proto/hapi/chart"
)

// ErrNoTable indicates that a chart does not have a matching table.
type ErrNoTable error

// ErrNoValue indicates that Values does not contain a key with a value
type ErrNoValue error

// GlobalKey is the name of the Values key that is used for storing global vars.
const GlobalKey = "global"

// Values represents a collection of chart values.
type Values map[string]interface{}

// YAML encodes the Values into a YAML string.
func (v Values) YAML() (string, error) {
	b, err := yaml.Marshal(v)
	return string(b), err
}

// Table gets a table (YAML subsection) from a Values object.
//
// The table is returned as a Values.
//
// Compound table names may be specified with dots:
//
//	foo.bar
//
// The above will be evaluated as "The table bar inside the table
// foo".
//
// An ErrNoTable is returned if the table does not exist.
func (v Values) Table(name string) (Values, error) {
	names := strings.Split(name, ".")
	table := v
	var err error

	for _, n := range names {
		table, err = tableLookup(table, n)
		if err != nil {
			return table, err
		}
	}
	return table, err
}

// AsMap is a utility function for converting Values to a map[string]interface{}.
//
// It protects against nil map panics.
func (v Values) AsMap() map[string]interface{} {
	if v == nil || len(v) == 0 {
		return map[string]interface{}{}
	}
	return v
}

// Encode writes serialized Values information to the given io.Writer.
func (v Values) Encode(w io.Writer) error {
	//return yaml.NewEncoder(w).Encode(v)
	out, err := yaml.Marshal(v)
	if err != nil {
		return err
	}
	_, err = w.Write(out)
	return err
}

func tableLookup(v Values, simple string) (Values, error) {
	v2, ok := v[simple]
	if !ok {
		return v, ErrNoTable(fmt.Errorf("no table named %q (%v)", simple, v))
	}
	if vv, ok := v2.(map[string]interface{}); ok {
		return vv, nil
	}

	// This catches a case where a value is of type Values, but doesn't (for some
	// reason) match the map[string]interface{}. This has been observed in the
	// wild, and might be a result of a nil map of type Values.
	if vv, ok := v2.(Values); ok {
		return vv, nil
	}

	var e ErrNoTable = fmt.Errorf("no table named %q", simple)
	return map[string]interface{}{}, e
}

// ReadValues will parse YAML byte data into a Values.
func ReadValues(data []byte) (vals Values, err error) {
	err = yaml.Unmarshal(data, &vals)
	if len(vals) == 0 {
		vals = Values{}
	}
	return
}

// ReadValuesFile will parse a YAML file into a map of values.
func ReadValuesFile(filename string) (Values, error) {
	data, err := ioutil.ReadFile(filename)
	if err != nil {
		return map[string]interface{}{}, err
	}
	return ReadValues(data)
}

// CoalesceValues coalesces all of the values in a chart (and its subcharts).
//
// Values are coalesced together using the following rules:
//
//	- Values in a higher level chart always override values in a lower-level
//		dependency chart
//	- Scalar values and arrays are replaced, maps are merged
//	- A chart has access to all of the variables for it, as well as all of
//		the values destined for its dependencies.
func CoalesceValues(chrt *chart.Chart, vals *chart.Config) (Values, error) {
	cvals := Values{}
	// Parse values if not nil. We merge these at the top level because
	// the passed-in values are in the same namespace as the parent chart.
	if vals != nil {
		evals, err := ReadValues([]byte(vals.Raw))
		if err != nil {
			return cvals, err
		}
		cvals, err = coalesce(chrt, evals)
		if err != nil {
			return cvals, err
		}
	}

	var err error
	cvals, err = coalesceDeps(chrt, cvals)
	return cvals, err
}

// coalesce coalesces the dest values and the chart values, giving priority to the dest values.
//
// This is a helper function for CoalesceValues.
func coalesce(ch *chart.Chart, dest map[string]interface{}) (map[string]interface{}, error) {
	var err error
	dest, err = coalesceValues(ch, dest)
	if err != nil {
		return dest, err
	}
	coalesceDeps(ch, dest)
	return dest, nil
}

// coalesceDeps coalesces the dependencies of the given chart.
func coalesceDeps(chrt *chart.Chart, dest map[string]interface{}) (map[string]interface{}, error) {
	for _, subchart := range chrt.Dependencies {
		if c, ok := dest[subchart.Metadata.Name]; !ok {
			// If dest doesn't already have the key, create it.
			dest[subchart.Metadata.Name] = map[string]interface{}{}
		} else if !istable(c) {
			return dest, fmt.Errorf("type mismatch on %s: %t", subchart.Metadata.Name, c)
		}
		if dv, ok := dest[subchart.Metadata.Name]; ok {
			dvmap := dv.(map[string]interface{})

			// Get globals out of dest and merge them into dvmap.
			coalesceGlobals(dvmap, dest)

			var err error
			// Now coalesce the rest of the values.
			dest[subchart.Metadata.Name], err = coalesce(subchart, dvmap)
			if err != nil {
				return dest, err
			}
		}
	}
	return dest, nil
}

// coalesceGlobals copies the globals out of src and merges them into dest.
//
// For convenience, returns dest.
func coalesceGlobals(dest, src map[string]interface{}) map[string]interface{} {
	var dg, sg map[string]interface{}

	if destglob, ok := dest[GlobalKey]; !ok {
		dg = map[string]interface{}{}
	} else if dg, ok = destglob.(map[string]interface{}); !ok {
		log.Printf("warning: skipping globals because destination %s is not a table.", GlobalKey)
		return dg
	}

	if srcglob, ok := src[GlobalKey]; !ok {
		sg = map[string]interface{}{}
	} else if sg, ok = srcglob.(map[string]interface{}); !ok {
		log.Printf("warning: skipping globals because source %s is not a table.", GlobalKey)
		return dg
	}

	// EXPERIMENTAL: In the past, we have disallowed globals to test tables. This
	// reverses that decision. It may somehow be possible to introduce a loop
	// here, but I haven't found a way. So for the time being, let's allow
	// tables in globals.
	for key, val := range sg {
		if istable(val) {
			vv := copyMap(val.(map[string]interface{}))
			if destv, ok := dg[key]; ok {
				if destvmap, ok := destv.(map[string]interface{}); ok {
					// Basically, we reverse order of coalesce here to merge
					// top-down.
					coalesceTables(vv, destvmap)
					dg[key] = vv
					continue
				} else {
					log.Printf("Conflict: cannot merge map onto non-map for %q. Skipping.", key)
				}
			} else {
				// Here there is no merge. We're just adding.
				dg[key] = vv
			}
		} else if dv, ok := dg[key]; ok && istable(dv) {
			// It's not clear if this condition can actually ever trigger.
			log.Printf("key %s is table. Skipping", key)
			continue
		}
		// TODO: Do we need to do any additional checking on the value?
		dg[key] = val
	}
	dest[GlobalKey] = dg
	return dest
}

func copyMap(src map[string]interface{}) map[string]interface{} {
	dest := make(map[string]interface{}, len(src))
	for k, v := range src {
		dest[k] = v
	}
	return dest
}

// coalesceValues builds up a values map for a particular chart.
//
// Values in v will override the values in the chart.
func coalesceValues(c *chart.Chart, v map[string]interface{}) (map[string]interface{}, error) {
	// If there are no values in the chart, we just return the given values
	if c.Values == nil || c.Values.Raw == "" {
		return v, nil
	}

	nv, err := ReadValues([]byte(c.Values.Raw))
	if err != nil {
		// On error, we return just the overridden values.
		// FIXME: We should log this error. It indicates that the YAML data
		// did not parse.
		return v, fmt.Errorf("error reading default values (%s): %s", c.Values.Raw, err)
	}

	for key, val := range nv {
		if _, ok := v[key]; !ok {
			// If the key is not in v, copy it from nv.
			v[key] = val
		} else if dest, ok := v[key].(map[string]interface{}); ok {
			// if v[key] is a table, merge nv's val table into v[key].
			src, ok := val.(map[string]interface{})
			if !ok {
				log.Printf("warning: skipped value for %s: Not a table.", key)
				continue
			}
			// Because v has higher precedence than nv, dest values override src
			// values.
			coalesceTables(dest, src)
		}
	}
	return v, nil
}

// coalesceTables merges a source map into a destination map.
//
// dest is considered authoritative.
func coalesceTables(dst, src map[string]interface{}) map[string]interface{} {
	// Because dest has higher precedence than src, dest values override src
	// values.
	for key, val := range src {
		if istable(val) {
			if innerdst, ok := dst[key]; !ok {
				dst[key] = val
			} else if istable(innerdst) {
				coalesceTables(innerdst.(map[string]interface{}), val.(map[string]interface{}))
			} else {
				log.Printf("warning: cannot overwrite table with non table for %s (%v)", key, val)
			}
			continue
		} else if dv, ok := dst[key]; ok && istable(dv) {
			log.Printf("warning: destination for %s is a table. Ignoring non-table value %v", key, val)
			continue
		} else if !ok { // <- ok is still in scope from preceding conditional.
			dst[key] = val
			continue
		}
	}
	return dst
}

// ReleaseOptions represents the additional release options needed
// for the composition of the final values struct
type ReleaseOptions struct {
	Name      string
	Time      *timestamp.Timestamp
	Namespace string
	IsUpgrade bool
	IsInstall bool
	Revision  int
}

// ToRenderValues composes the struct from the data coming from the Releases, Charts and Values files
//
// WARNING: This function is deprecated for Helm > 2.1.99 Use ToRenderValuesCaps() instead. It will
// remain in the codebase to stay SemVer compliant.
//
// In Helm 3.0, this will be changed to accept Capabilities as a fourth parameter.
func ToRenderValues(chrt *chart.Chart, chrtVals *chart.Config, options ReleaseOptions) (Values, error) {
	caps := &Capabilities{APIVersions: DefaultVersionSet}
	return ToRenderValuesCaps(chrt, chrtVals, options, caps)
}

// ToRenderValuesCaps composes the struct from the data coming from the Releases, Charts and Values files
//
// This takes both ReleaseOptions and Capabilities to merge into the render values.
func ToRenderValuesCaps(chrt *chart.Chart, chrtVals *chart.Config, options ReleaseOptions, caps *Capabilities) (Values, error) {

	top := map[string]interface{}{
		"Release": map[string]interface{}{
			"Name":      options.Name,
			"Time":      options.Time,
			"Namespace": options.Namespace,
			"IsUpgrade": options.IsUpgrade,
			"IsInstall": options.IsInstall,
			"Revision":  options.Revision,
			"Service":   "Tiller",
		},
		"Chart":        chrt.Metadata,
		"Files":        NewFiles(chrt.Files),
		"Capabilities": caps,
	}

	vals, err := CoalesceValues(chrt, chrtVals)
	if err != nil {
		return top, err
	}

	top["Values"] = vals
	return top, nil
}

// istable is a special-purpose function to see if the present thing matches the definition of a YAML table.
func istable(v interface{}) bool {
	_, ok := v.(map[string]interface{})
	return ok
}

// PathValue takes a yaml path with . notation and returns the value if exists
func (v Values) PathValue(ypath string) (interface{}, error) {
	if len(ypath) == 0 {
		return nil, error(fmt.Errorf("yaml path string cannot be zero length"))
	}
	yps := strings.Split(ypath, ".")
	if len(ypath) == 1 {
		// if exists must be root key not table
		vals := v.AsMap()
		k := yps[0]
		if _, ok := vals[k]; ok && !istable(vals[k]) {
			// key found
			return vals[yps[0]], nil
		}
		// key not found
		return nil, ErrNoValue(fmt.Errorf("%v is not a value", k))
	}
	table := yps[:len(yps)-1]
	st := strings.Join(table, ".")
	key := yps[len(yps)-1:]
	sk := string(key[0])

	t, err := v.Table(st)
	if err != nil {
		//no table
		return nil, ErrNoValue(fmt.Errorf("%v is not a value", sk))
	}
	if k, ok := t[sk]; ok && !istable(k) {
		// key found
		return k, nil
	}

	// key not found
	return nil, ErrNoValue(fmt.Errorf("key not found: %s", sk))
}
<commit_msg>feat(helm): Check len of correct slice<commit_after>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

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 chartutil

import (
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"strings"

	"github.com/ghodss/yaml"
	"github.com/golang/protobuf/ptypes/timestamp"
	"k8s.io/helm/pkg/proto/hapi/chart"
)

// ErrNoTable indicates that a chart does not have a matching table.
type ErrNoTable error

// ErrNoValue indicates that Values does not contain a key with a value
type ErrNoValue error

// GlobalKey is the name of the Values key that is used for storing global vars.
const GlobalKey = "global"

// Values represents a collection of chart values.
type Values map[string]interface{}

// YAML encodes the Values into a YAML string.
func (v Values) YAML() (string, error) {
	b, err := yaml.Marshal(v)
	return string(b), err
}

// Table gets a table (YAML subsection) from a Values object.
//
// The table is returned as a Values.
//
// Compound table names may be specified with dots:
//
//	foo.bar
//
// The above will be evaluated as "The table bar inside the table
// foo".
//
// An ErrNoTable is returned if the table does not exist.
func (v Values) Table(name string) (Values, error) {
	names := strings.Split(name, ".")
	table := v
	var err error

	for _, n := range names {
		table, err = tableLookup(table, n)
		if err != nil {
			return table, err
		}
	}
	return table, err
}

// AsMap is a utility function for converting Values to a map[string]interface{}.
//
// It protects against nil map panics.
func (v Values) AsMap() map[string]interface{} {
	if v == nil || len(v) == 0 {
		return map[string]interface{}{}
	}
	return v
}

// Encode writes serialized Values information to the given io.Writer.
func (v Values) Encode(w io.Writer) error {
	//return yaml.NewEncoder(w).Encode(v)
	out, err := yaml.Marshal(v)
	if err != nil {
		return err
	}
	_, err = w.Write(out)
	return err
}

func tableLookup(v Values, simple string) (Values, error) {
	v2, ok := v[simple]
	if !ok {
		return v, ErrNoTable(fmt.Errorf("no table named %q (%v)", simple, v))
	}
	if vv, ok := v2.(map[string]interface{}); ok {
		return vv, nil
	}

	// This catches a case where a value is of type Values, but doesn't (for some
	// reason) match the map[string]interface{}. This has been observed in the
	// wild, and might be a result of a nil map of type Values.
	if vv, ok := v2.(Values); ok {
		return vv, nil
	}

	var e ErrNoTable = fmt.Errorf("no table named %q", simple)
	return map[string]interface{}{}, e
}

// ReadValues will parse YAML byte data into a Values.
func ReadValues(data []byte) (vals Values, err error) {
	err = yaml.Unmarshal(data, &vals)
	if len(vals) == 0 {
		vals = Values{}
	}
	return
}

// ReadValuesFile will parse a YAML file into a map of values.
func ReadValuesFile(filename string) (Values, error) {
	data, err := ioutil.ReadFile(filename)
	if err != nil {
		return map[string]interface{}{}, err
	}
	return ReadValues(data)
}

// CoalesceValues coalesces all of the values in a chart (and its subcharts).
//
// Values are coalesced together using the following rules:
//
//	- Values in a higher level chart always override values in a lower-level
//		dependency chart
//	- Scalar values and arrays are replaced, maps are merged
//	- A chart has access to all of the variables for it, as well as all of
//		the values destined for its dependencies.
func CoalesceValues(chrt *chart.Chart, vals *chart.Config) (Values, error) {
	cvals := Values{}
	// Parse values if not nil. We merge these at the top level because
	// the passed-in values are in the same namespace as the parent chart.
	if vals != nil {
		evals, err := ReadValues([]byte(vals.Raw))
		if err != nil {
			return cvals, err
		}
		cvals, err = coalesce(chrt, evals)
		if err != nil {
			return cvals, err
		}
	}

	var err error
	cvals, err = coalesceDeps(chrt, cvals)
	return cvals, err
}

// coalesce coalesces the dest values and the chart values, giving priority to the dest values.
//
// This is a helper function for CoalesceValues.
func coalesce(ch *chart.Chart, dest map[string]interface{}) (map[string]interface{}, error) {
	var err error
	dest, err = coalesceValues(ch, dest)
	if err != nil {
		return dest, err
	}
	coalesceDeps(ch, dest)
	return dest, nil
}

// coalesceDeps coalesces the dependencies of the given chart.
func coalesceDeps(chrt *chart.Chart, dest map[string]interface{}) (map[string]interface{}, error) {
	for _, subchart := range chrt.Dependencies {
		if c, ok := dest[subchart.Metadata.Name]; !ok {
			// If dest doesn't already have the key, create it.
			dest[subchart.Metadata.Name] = map[string]interface{}{}
		} else if !istable(c) {
			return dest, fmt.Errorf("type mismatch on %s: %t", subchart.Metadata.Name, c)
		}
		if dv, ok := dest[subchart.Metadata.Name]; ok {
			dvmap := dv.(map[string]interface{})

			// Get globals out of dest and merge them into dvmap.
			coalesceGlobals(dvmap, dest)

			var err error
			// Now coalesce the rest of the values.
			dest[subchart.Metadata.Name], err = coalesce(subchart, dvmap)
			if err != nil {
				return dest, err
			}
		}
	}
	return dest, nil
}

// coalesceGlobals copies the globals out of src and merges them into dest.
//
// For convenience, returns dest.
func coalesceGlobals(dest, src map[string]interface{}) map[string]interface{} {
	var dg, sg map[string]interface{}

	if destglob, ok := dest[GlobalKey]; !ok {
		dg = map[string]interface{}{}
	} else if dg, ok = destglob.(map[string]interface{}); !ok {
		log.Printf("warning: skipping globals because destination %s is not a table.", GlobalKey)
		return dg
	}

	if srcglob, ok := src[GlobalKey]; !ok {
		sg = map[string]interface{}{}
	} else if sg, ok = srcglob.(map[string]interface{}); !ok {
		log.Printf("warning: skipping globals because source %s is not a table.", GlobalKey)
		return dg
	}

	// EXPERIMENTAL: In the past, we have disallowed globals to test tables. This
	// reverses that decision. It may somehow be possible to introduce a loop
	// here, but I haven't found a way. So for the time being, let's allow
	// tables in globals.
	for key, val := range sg {
		if istable(val) {
			vv := copyMap(val.(map[string]interface{}))
			if destv, ok := dg[key]; ok {
				if destvmap, ok := destv.(map[string]interface{}); ok {
					// Basically, we reverse order of coalesce here to merge
					// top-down.
					coalesceTables(vv, destvmap)
					dg[key] = vv
					continue
				} else {
					log.Printf("Conflict: cannot merge map onto non-map for %q. Skipping.", key)
				}
			} else {
				// Here there is no merge. We're just adding.
				dg[key] = vv
			}
		} else if dv, ok := dg[key]; ok && istable(dv) {
			// It's not clear if this condition can actually ever trigger.
			log.Printf("key %s is table. Skipping", key)
			continue
		}
		// TODO: Do we need to do any additional checking on the value?
		dg[key] = val
	}
	dest[GlobalKey] = dg
	return dest
}

func copyMap(src map[string]interface{}) map[string]interface{} {
	dest := make(map[string]interface{}, len(src))
	for k, v := range src {
		dest[k] = v
	}
	return dest
}

// coalesceValues builds up a values map for a particular chart.
//
// Values in v will override the values in the chart.
func coalesceValues(c *chart.Chart, v map[string]interface{}) (map[string]interface{}, error) {
	// If there are no values in the chart, we just return the given values
	if c.Values == nil || c.Values.Raw == "" {
		return v, nil
	}

	nv, err := ReadValues([]byte(c.Values.Raw))
	if err != nil {
		// On error, we return just the overridden values.
		// FIXME: We should log this error. It indicates that the YAML data
		// did not parse.
		return v, fmt.Errorf("error reading default values (%s): %s", c.Values.Raw, err)
	}

	for key, val := range nv {
		if _, ok := v[key]; !ok {
			// If the key is not in v, copy it from nv.
			v[key] = val
		} else if dest, ok := v[key].(map[string]interface{}); ok {
			// if v[key] is a table, merge nv's val table into v[key].
			src, ok := val.(map[string]interface{})
			if !ok {
				log.Printf("warning: skipped value for %s: Not a table.", key)
				continue
			}
			// Because v has higher precedence than nv, dest values override src
			// values.
			coalesceTables(dest, src)
		}
	}
	return v, nil
}

// coalesceTables merges a source map into a destination map.
//
// dest is considered authoritative.
func coalesceTables(dst, src map[string]interface{}) map[string]interface{} {
	// Because dest has higher precedence than src, dest values override src
	// values.
	for key, val := range src {
		if istable(val) {
			if innerdst, ok := dst[key]; !ok {
				dst[key] = val
			} else if istable(innerdst) {
				coalesceTables(innerdst.(map[string]interface{}), val.(map[string]interface{}))
			} else {
				log.Printf("warning: cannot overwrite table with non table for %s (%v)", key, val)
			}
			continue
		} else if dv, ok := dst[key]; ok && istable(dv) {
			log.Printf("warning: destination for %s is a table. Ignoring non-table value %v", key, val)
			continue
		} else if !ok { // <- ok is still in scope from preceding conditional.
			dst[key] = val
			continue
		}
	}
	return dst
}

// ReleaseOptions represents the additional release options needed
// for the composition of the final values struct
type ReleaseOptions struct {
	Name      string
	Time      *timestamp.Timestamp
	Namespace string
	IsUpgrade bool
	IsInstall bool
	Revision  int
}

// ToRenderValues composes the struct from the data coming from the Releases, Charts and Values files
//
// WARNING: This function is deprecated for Helm > 2.1.99 Use ToRenderValuesCaps() instead. It will
// remain in the codebase to stay SemVer compliant.
//
// In Helm 3.0, this will be changed to accept Capabilities as a fourth parameter.
func ToRenderValues(chrt *chart.Chart, chrtVals *chart.Config, options ReleaseOptions) (Values, error) {
	caps := &Capabilities{APIVersions: DefaultVersionSet}
	return ToRenderValuesCaps(chrt, chrtVals, options, caps)
}

// ToRenderValuesCaps composes the struct from the data coming from the Releases, Charts and Values files
//
// This takes both ReleaseOptions and Capabilities to merge into the render values.
func ToRenderValuesCaps(chrt *chart.Chart, chrtVals *chart.Config, options ReleaseOptions, caps *Capabilities) (Values, error) {

	top := map[string]interface{}{
		"Release": map[string]interface{}{
			"Name":      options.Name,
			"Time":      options.Time,
			"Namespace": options.Namespace,
			"IsUpgrade": options.IsUpgrade,
			"IsInstall": options.IsInstall,
			"Revision":  options.Revision,
			"Service":   "Tiller",
		},
		"Chart":        chrt.Metadata,
		"Files":        NewFiles(chrt.Files),
		"Capabilities": caps,
	}

	vals, err := CoalesceValues(chrt, chrtVals)
	if err != nil {
		return top, err
	}

	top["Values"] = vals
	return top, nil
}

// istable is a special-purpose function to see if the present thing matches the definition of a YAML table.
func istable(v interface{}) bool {
	_, ok := v.(map[string]interface{})
	return ok
}

// PathValue takes a yaml path with . notation and returns the value if exists
func (v Values) PathValue(ypath string) (interface{}, error) {
	if len(ypath) == 0 {
		return nil, error(fmt.Errorf("yaml path string cannot be zero length"))
	}
	yps := strings.Split(ypath, ".")
	if len(yps) == 1 {
		// if exists must be root key not table
		vals := v.AsMap()
		k := yps[0]
		if _, ok := vals[k]; ok && !istable(vals[k]) {
			// key found
			return vals[yps[0]], nil
		}
		// key not found
		return nil, ErrNoValue(fmt.Errorf("%v is not a value", k))
	}
	table := yps[:len(yps)-1]
	st := strings.Join(table, ".")
	key := yps[len(yps)-1:]
	sk := string(key[0])

	t, err := v.Table(st)
	if err != nil {
		//no table
		return nil, ErrNoValue(fmt.Errorf("%v is not a value", sk))
	}
	if k, ok := t[sk]; ok && !istable(k) {
		// key found
		return k, nil
	}

	// key not found
	return nil, ErrNoValue(fmt.Errorf("key not found: %s", sk))
}
<|endoftext|>


<commit_before>package schema

import (
	"time"

	"github.com/willf/bitset"
)

// FileUpload is the tracking information for an individual file upload.
type FileUpload struct {
	Name        string         `gorethink:"name"`         // File name on remote system
	Checksum    string         `gorethink:"checksum"`     // Computed file checksum
	Size        int64          `gorethink:"size"`         // Size of file on remote system
	Birthtime   time.Time      `gorethink:"birthtime"`    // When was FileUpload started
	MTime       time.Time      `gorethink:"mtime"`        // Last time this entry was modified
	RemoteMTime time.Time      `gorethink:"remote_mtime"` // CTime of the remote file
	ChunkSize   int            `gorethink:"chunk_size"`   // Chunk transfer size
	ChunkCount  int            `gorethink:"chunk_count"`  // Number of chunks expected
	Blocks      *bitset.BitSet // Block state. If set block as has been uploaded
	BitString   []byte
}

// A Upload models a user upload request. It allows for users to restart
// upload requests.
type Upload struct {
	ID            string     `gorethink:"id,omitempty"`
	Owner         string     `gorethink:"owner"`          // Who started the upload
	DirectoryID   string     `gorethink:"directory_id"`   // Directory to upload to
	DirectoryName string     `gorethink:"directory_name"` // Name of directory
	ProjectID     string     `gorethink:"project_id"`     // Project to upload to
	ProjectOwner  string     `gorethink:"project_owner"`  // Owner of project
	ProjectName   string     `gorethink:"project_name"`   // Name of project
	Birthtime     time.Time  `gorethink:"birthtime"`      // When was upload started
	Host          string     `gorethink:"host"`           // Host requesting the upload
	File          FileUpload `gorethink:"file"`           // File being uploaded
}

// private type to hang helper methods off of.
type uploadList struct{}

// Uploads gives access to helper routines that work on lists of uploads.
var Uploads uploadList

// Find will return a matching Upload in a list of uploads when the match func returns true.
func (u uploadList) Find(uploads []Upload, match func(upload Upload) bool) *Upload {
	for _, upload := range uploads {
		if match(upload) {
			return &upload
		}
	}

	return nil
}

// uploadCreater allows for chaining creation of an upload.
type uploadCreater struct {
	upload Upload
}

// CUpload creates a new uploadCreater.
func CUpload() *uploadCreater {
	u := &uploadCreater{}
	now := time.Now()
	u.upload.Birthtime = now
	u.upload.File.Birthtime = now
	u.upload.File.MTime = now
	u.upload.File.RemoteMTime = now
	return u
}

// Owner sets the Upload owner field.
func (c *uploadCreater) Owner(owner string) *uploadCreater {
	c.upload.Owner = owner
	return c
}

// Directory sets the Upload DirectoryID and DirectoryName fields.
func (c *uploadCreater) Directory(id, name string) *uploadCreater {
	c.upload.DirectoryID = id
	c.upload.DirectoryName = name
	return c
}

// Project sets the Upload ProjectID and ProjectName fields.
func (c *uploadCreater) Project(id, name string) *uploadCreater {
	c.upload.ProjectID = id
	c.upload.ProjectName = name
	return c
}

// ProjectOnwer sets the Upload ProjectOwner field.
func (c *uploadCreater) ProjectOwner(owner string) *uploadCreater {
	c.upload.ProjectOwner = owner
	return c
}

// Birthtime sets the Upload Birthtime field.
func (c *uploadCreater) Birthtime(birthtime time.Time) *uploadCreater {
	c.upload.Birthtime = birthtime
	return c
}

// Host sets the Upload Host field.
func (c *uploadCreater) Host(host string) *uploadCreater {
	c.upload.Host = host
	return c
}

// FName sets the Upload.File.Name field.
func (c *uploadCreater) FName(name string) *uploadCreater {
	c.upload.File.Name = name
	return c
}

// FChecksum sets the Upload.File.Checksum field.
func (c *uploadCreater) FChecksum(checksum string) *uploadCreater {
	c.upload.File.Checksum = checksum
	return c
}

// FSize sets the Upload.File.Size field.
func (c *uploadCreater) FSize(size int64) *uploadCreater {
	c.upload.File.Size = size
	return c
}

// FTime sets the Upload.File.Birthtime and Upload.File.MTime fields.
func (c *uploadCreater) FTime(t time.Time) *uploadCreater {
	c.upload.File.Birthtime = t
	c.upload.File.MTime = t
	return c
}

// FRemoteCTime sets the Upload.File.RemoteCTime field.
func (c *uploadCreater) FRemoteMTime(t time.Time) *uploadCreater {
	c.upload.File.RemoteMTime = t
	return c
}

// FChunk sets the Upload.File.ChunkSize and the Upload.File.ChunkCount fields.
func (c *uploadCreater) FChunk(size, count int) *uploadCreater {
	c.upload.File.ChunkSize = size
	c.upload.File.ChunkCount = count
	return c
}

// FBlocks sets the Upload.File.Blocks field.
func (c *uploadCreater) FBlocks(blocks *bitset.BitSet) *uploadCreater {
	c.upload.File.Blocks = blocks
	c.upload.File.BitString, _ = blocks.MarshalJSON()
	return c
}

// Create creates a new Upload instance from the values given to the
// the creater.
func (c *uploadCreater) Create() Upload {
	return c.upload
}
<commit_msg>Update comment for FBlocks to specify that the BitString field is also set by this method.<commit_after>package schema

import (
	"time"

	"github.com/willf/bitset"
)

// FileUpload is the tracking information for an individual file upload.
type FileUpload struct {
	Name        string         `gorethink:"name"`         // File name on remote system
	Checksum    string         `gorethink:"checksum"`     // Computed file checksum
	Size        int64          `gorethink:"size"`         // Size of file on remote system
	Birthtime   time.Time      `gorethink:"birthtime"`    // When was FileUpload started
	MTime       time.Time      `gorethink:"mtime"`        // Last time this entry was modified
	RemoteMTime time.Time      `gorethink:"remote_mtime"` // CTime of the remote file
	ChunkSize   int            `gorethink:"chunk_size"`   // Chunk transfer size
	ChunkCount  int            `gorethink:"chunk_count"`  // Number of chunks expected
	Blocks      *bitset.BitSet // Block state. If set block as has been uploaded
	BitString   []byte
}

// A Upload models a user upload request. It allows for users to restart
// upload requests.
type Upload struct {
	ID            string     `gorethink:"id,omitempty"`
	Owner         string     `gorethink:"owner"`          // Who started the upload
	DirectoryID   string     `gorethink:"directory_id"`   // Directory to upload to
	DirectoryName string     `gorethink:"directory_name"` // Name of directory
	ProjectID     string     `gorethink:"project_id"`     // Project to upload to
	ProjectOwner  string     `gorethink:"project_owner"`  // Owner of project
	ProjectName   string     `gorethink:"project_name"`   // Name of project
	Birthtime     time.Time  `gorethink:"birthtime"`      // When was upload started
	Host          string     `gorethink:"host"`           // Host requesting the upload
	File          FileUpload `gorethink:"file"`           // File being uploaded
}

// private type to hang helper methods off of.
type uploadList struct{}

// Uploads gives access to helper routines that work on lists of uploads.
var Uploads uploadList

// Find will return a matching Upload in a list of uploads when the match func returns true.
func (u uploadList) Find(uploads []Upload, match func(upload Upload) bool) *Upload {
	for _, upload := range uploads {
		if match(upload) {
			return &upload
		}
	}

	return nil
}

// uploadCreater allows for chaining creation of an upload.
type uploadCreater struct {
	upload Upload
}

// CUpload creates a new uploadCreater.
func CUpload() *uploadCreater {
	u := &uploadCreater{}
	now := time.Now()
	u.upload.Birthtime = now
	u.upload.File.Birthtime = now
	u.upload.File.MTime = now
	u.upload.File.RemoteMTime = now
	return u
}

// Owner sets the Upload owner field.
func (c *uploadCreater) Owner(owner string) *uploadCreater {
	c.upload.Owner = owner
	return c
}

// Directory sets the Upload DirectoryID and DirectoryName fields.
func (c *uploadCreater) Directory(id, name string) *uploadCreater {
	c.upload.DirectoryID = id
	c.upload.DirectoryName = name
	return c
}

// Project sets the Upload ProjectID and ProjectName fields.
func (c *uploadCreater) Project(id, name string) *uploadCreater {
	c.upload.ProjectID = id
	c.upload.ProjectName = name
	return c
}

// ProjectOnwer sets the Upload ProjectOwner field.
func (c *uploadCreater) ProjectOwner(owner string) *uploadCreater {
	c.upload.ProjectOwner = owner
	return c
}

// Birthtime sets the Upload Birthtime field.
func (c *uploadCreater) Birthtime(birthtime time.Time) *uploadCreater {
	c.upload.Birthtime = birthtime
	return c
}

// Host sets the Upload Host field.
func (c *uploadCreater) Host(host string) *uploadCreater {
	c.upload.Host = host
	return c
}

// FName sets the Upload.File.Name field.
func (c *uploadCreater) FName(name string) *uploadCreater {
	c.upload.File.Name = name
	return c
}

// FChecksum sets the Upload.File.Checksum field.
func (c *uploadCreater) FChecksum(checksum string) *uploadCreater {
	c.upload.File.Checksum = checksum
	return c
}

// FSize sets the Upload.File.Size field.
func (c *uploadCreater) FSize(size int64) *uploadCreater {
	c.upload.File.Size = size
	return c
}

// FTime sets the Upload.File.Birthtime and Upload.File.MTime fields.
func (c *uploadCreater) FTime(t time.Time) *uploadCreater {
	c.upload.File.Birthtime = t
	c.upload.File.MTime = t
	return c
}

// FRemoteCTime sets the Upload.File.RemoteCTime field.
func (c *uploadCreater) FRemoteMTime(t time.Time) *uploadCreater {
	c.upload.File.RemoteMTime = t
	return c
}

// FChunk sets the Upload.File.ChunkSize and the Upload.File.ChunkCount fields.
func (c *uploadCreater) FChunk(size, count int) *uploadCreater {
	c.upload.File.ChunkSize = size
	c.upload.File.ChunkCount = count
	return c
}

// FBlocks sets the Upload.File.Blocks field. It also sets the BitString
// field with a byte array representation of the bitset.
func (c *uploadCreater) FBlocks(blocks *bitset.BitSet) *uploadCreater {
	c.upload.File.Blocks = blocks
	c.upload.File.BitString, _ = blocks.MarshalJSON()
	return c
}

// Create creates a new Upload instance from the values given to the
// the creater.
func (c *uploadCreater) Create() Upload {
	return c.upload
}
<|endoftext|>


<commit_before>package proto

import (
	"bufio"
	"bytes"
	"io"
	"junta/assert"
	"os"
	"reflect"
	"testing"
)

// == Testing =============================

type ErroneousWriter struct {
	// No exported fields
	io.Writer
	which int
}

func (ew *ErroneousWriter) Write(bytes []byte) (int, os.Error) {
	if ew.which == 0 {
		return 0, os.NewError("BOOM!")
	}
	ew.which--
	return ew.Writer.Write(bytes)
}

type encTest struct {
	encoding string
	data     interface{}
}

var encTests = []encTest{
	{":0\r\n", int(0)},
	{":0\r\n", int8(0)},
	{":0\r\n", int16(0)},
	{":0\r\n", int32(0)},
	{":0\r\n", int64(0)},
	{":0\r\n", uint(0)},
	{":0\r\n", uint8(0)}, // aka byte
	{":0\r\n", uint16(0)},
	{":0\r\n", uint32(0)},
	{":0\r\n", uint64(0)},

	{"$3\r\nfoo\r\n", []byte{'f', 'o', 'o'}},
	{"$3\r\nfoo\r\n", "foo"},
	{"+hi\r\n", Line("hi")},
	{"-hi\r\n", os.NewError("hi")},
	{"$-1\r\n", nil},
	{"*2\r\n$1\r\na\r\n$1\r\nb\r\n", []interface{}{[]byte{'a'}, []byte{'b'}}},
	{"*2\r\n$3\r\nGET\r\n$3\r\nFOO\r\n", []string{"GET", "FOO"}},
	{"*2\r\n:1\r\n*1\r\n:1\r\n", []interface{}{1, []interface{}{1}}},
}

var decTests = []encTest{
	{":0\r\n", int64(0)},
	{":1\r\n", int64(1)},
	{":-1\r\n", int64(-1)},
	{":18446744073709551611\r\n", uint64(18446744073709551611)},
	{"$3\r\nfoo\r\n", []byte{'f', 'o', 'o'}},
	{"+hi\r\n", Line("hi")},
	{"-hi\r\n", ResponseError("hi")},
	{"$-1\r\n", nil},
	{"*2\r\n$1\r\na\r\n$1\r\nb\r\n", []interface{}{[]byte{'a'}, []byte{'b'}}},
	{"*1\r\n*1\r\n:1\r\n", []interface{}{[]interface{}{int64(1)}}},
	{"\r\n:0\r\n", int64(0)}, // ignore blank lines
}

var decErrTests = []string{
	":111118446744073709551611\r\n", // out of range for uint64
	"$18446744073709551611\r\n", // out of range for int
	"*18446744073709551611\r\n", // out of range for int
}

func TestProtoEncodeVal(t *testing.T) {
	for _, e := range encTests {
		b := new(bytes.Buffer)
		err := encode(b, e.data)
		if err != nil {
			t.Error("unexpected err:", err)
			continue
		}
		if e.encoding != string(b.Bytes()) {
			t.Errorf("expected %q", e.encoding)
			t.Errorf("     got %q", b.String())
		}
	}
}

func TestProtoEncodeHeaderError(t *testing.T) {
	b := new(bytes.Buffer)
	ew := &ErroneousWriter{b, 0}

	err := encode(ew, []string{"GET", "FOO"})
	assert.T(t, err != nil, err)
}

func TestProtoEncodeBodyError(t *testing.T) {
	b := new(bytes.Buffer)
	ew := &ErroneousWriter{b, 1}

	err := encode(ew, []string{"GET", "FOO"})
	assert.T(t, err != nil, err)
}

func TestProtoDecodeEmptyLine(t *testing.T) {
	b := new(bytes.Buffer)
	_, err := decode(bufio.NewReader(b))
	assert.T(t, err != nil, err)
}

func TestProtoDecodeVal(t *testing.T) {
	for _, e := range decTests {
		b := bytes.NewBufferString(e.encoding)
		r := bufio.NewReader(b)
		data, err := decode(r)
		if err != nil {
			t.Errorf("in %q,", e.encoding)
			t.Error("unexpected err:", err)
			continue
		}
		if !reflect.DeepEqual(e.data, data) {
			t.Errorf("in %q,", e.encoding)
			t.Errorf("expected %T, %v", e.data, e.data)
			t.Errorf("     got %T, %v", data, data)
		}
	}
}

func TestProtoDecodeShort(t *testing.T) {
	for _, e := range decTests {
		n := len(e.encoding)
		for i := 0; i < n; i++ {
			s := e.encoding[0:i]
			r := bufio.NewReader(bytes.NewBufferString(s))
			_, err := decode(r)
			if err == nil {
				t.Errorf("expected an error from %q", s)
			}
		}
	}
}

func TestProtoDecodeErr(t *testing.T) {
	for _, s := range decErrTests {
		r := bufio.NewReader(bytes.NewBufferString(s))
		_, err := decode(r)
		if err == nil {
			t.Errorf("expected an error from %q", s)
		}
	}
}
<commit_msg>proto: test err reporting on short writes<commit_after>package proto

import (
	"bufio"
	"bytes"
	"io"
	"junta/assert"
	"junta/test"
	"os"
	"reflect"
	"testing"
)

// == Testing =============================

type ErroneousWriter struct {
	// No exported fields
	io.Writer
	which int
}

func (ew *ErroneousWriter) Write(bytes []byte) (int, os.Error) {
	if ew.which == 0 {
		return 0, os.NewError("BOOM!")
	}
	ew.which--
	return ew.Writer.Write(bytes)
}

type encTest struct {
	encoding string
	data     interface{}
}

var encTests = []encTest{
	{":0\r\n", int(0)},
	{":0\r\n", int8(0)},
	{":0\r\n", int16(0)},
	{":0\r\n", int32(0)},
	{":0\r\n", int64(0)},
	{":0\r\n", uint(0)},
	{":0\r\n", uint8(0)}, // aka byte
	{":0\r\n", uint16(0)},
	{":0\r\n", uint32(0)},
	{":0\r\n", uint64(0)},

	{"$3\r\nfoo\r\n", []byte{'f', 'o', 'o'}},
	{"$3\r\nfoo\r\n", "foo"},
	{"+hi\r\n", Line("hi")},
	{"-hi\r\n", os.NewError("hi")},
	{"$-1\r\n", nil},
	{"*2\r\n$1\r\na\r\n$1\r\nb\r\n", []interface{}{[]byte{'a'}, []byte{'b'}}},
	{"*2\r\n$3\r\nGET\r\n$3\r\nFOO\r\n", []string{"GET", "FOO"}},
	{"*2\r\n:1\r\n*1\r\n:1\r\n", []interface{}{1, []interface{}{1}}},
}

var decTests = []encTest{
	{":0\r\n", int64(0)},
	{":1\r\n", int64(1)},
	{":-1\r\n", int64(-1)},
	{":18446744073709551611\r\n", uint64(18446744073709551611)},
	{"$3\r\nfoo\r\n", []byte{'f', 'o', 'o'}},
	{"+hi\r\n", Line("hi")},
	{"-hi\r\n", ResponseError("hi")},
	{"$-1\r\n", nil},
	{"*2\r\n$1\r\na\r\n$1\r\nb\r\n", []interface{}{[]byte{'a'}, []byte{'b'}}},
	{"*1\r\n*1\r\n:1\r\n", []interface{}{[]interface{}{int64(1)}}},
	{"\r\n:0\r\n", int64(0)}, // ignore blank lines
}

var decErrTests = []string{
	":111118446744073709551611\r\n", // out of range for uint64
	"$18446744073709551611\r\n", // out of range for int
	"*18446744073709551611\r\n", // out of range for int
}

func TestProtoEncodeVal(t *testing.T) {
	for _, e := range encTests {
		b := new(bytes.Buffer)
		err := encode(b, e.data)
		if err != nil {
			t.Error("unexpected err:", err)
			continue
		}
		if e.encoding != string(b.Bytes()) {
			t.Errorf("expected %q", e.encoding)
			t.Errorf("     got %q", b.String())
		}
	}
}

func TestProtoEncodeShort(t *testing.T) {
	for _, e := range encTests {
		n := len(e.encoding)
		for i := 0; i < n; i++ {
			t.Logf("trying %d bytes", i)
			w := test.ErrWriter{i}
			err := encode(&w, e.data)
			if err == nil {
				t.Errorf("expected an error encoding %T %v in %d bytes", e.data, e.data, i)
			}
		}
	}
}

func TestProtoEncodeHeaderError(t *testing.T) {
	b := new(bytes.Buffer)
	ew := &ErroneousWriter{b, 0}

	err := encode(ew, []string{"GET", "FOO"})
	assert.T(t, err != nil, err)
}

func TestProtoEncodeBodyError(t *testing.T) {
	b := new(bytes.Buffer)
	ew := &ErroneousWriter{b, 1}

	err := encode(ew, []string{"GET", "FOO"})
	assert.T(t, err != nil, err)
}

func TestProtoDecodeEmptyLine(t *testing.T) {
	b := new(bytes.Buffer)
	_, err := decode(bufio.NewReader(b))
	assert.T(t, err != nil, err)
}

func TestProtoDecodeVal(t *testing.T) {
	for _, e := range decTests {
		b := bytes.NewBufferString(e.encoding)
		r := bufio.NewReader(b)
		data, err := decode(r)
		if err != nil {
			t.Errorf("in %q,", e.encoding)
			t.Error("unexpected err:", err)
			continue
		}
		if !reflect.DeepEqual(e.data, data) {
			t.Errorf("in %q,", e.encoding)
			t.Errorf("expected %T, %v", e.data, e.data)
			t.Errorf("     got %T, %v", data, data)
		}
	}
}

func TestProtoDecodeShort(t *testing.T) {
	for _, e := range decTests {
		n := len(e.encoding)
		for i := 0; i < n; i++ {
			s := e.encoding[0:i]
			r := bufio.NewReader(bytes.NewBufferString(s))
			_, err := decode(r)
			if err == nil {
				t.Errorf("expected an error from %q", s)
			}
		}
	}
}

func TestProtoDecodeErr(t *testing.T) {
	for _, s := range decErrTests {
		r := bufio.NewReader(bytes.NewBufferString(s))
		_, err := decode(r)
		if err == nil {
			t.Errorf("expected an error from %q", s)
		}
	}
}
<|endoftext|>


<commit_before>//
// Last.Backend LLC CONFIDENTIAL
// __________________
//
// [2014] - [2018] Last.Backend LLC
// All Rights Reserved.
//
// NOTICE:  All information contained herein is, and remains
// the property of Last.Backend LLC and its suppliers,
// if any.  The intellectual and technical concepts contained
// herein are proprietary to Last.Backend LLC
// and its suppliers and may be covered by Russian Federation and Foreign Patents,
// patents in process, and are protected by trade secret or copyright law.
// Dissemination of this information or reproduction of this material
// is strictly forbidden unless prior written permission is obtained
// from Last.Backend LLC.
//

package mock

import (
	"context"
	"fmt"
	"github.com/lastbackend/lastbackend/pkg/distribution/errors"
	"github.com/lastbackend/lastbackend/pkg/distribution/types"
	"github.com/lastbackend/lastbackend/pkg/storage/storage"
	"github.com/lastbackend/lastbackend/pkg/storage/store"
	"strings"
)

// Pod Service type for interface in interfaces folder
type PodStorage struct {
	storage.Pod
	data map[string]*types.Pod
}

// Get pod from storage
func (s *PodStorage) Get(ctx context.Context, namespace, service, deployment, name string) (*types.Pod, error) {
	if ns, ok := s.data[s.keyCreate(namespace, service, deployment, name)]; ok {
		return ns, nil
	}
	return nil, errors.New(store.ErrEntityNotFound)
}

// ListByNamespace returns pod list from storage by namespace
func (s *PodStorage) ListByNamespace(ctx context.Context, namespace string) (map[string]*types.Pod, error) {
	list := make(map[string]*types.Pod, 0)

	prefix := fmt.Sprintf("%s:", namespace)
	for _, d := range s.data {

		if strings.HasPrefix(s.keyGet(d), prefix) {
			list[s.keyGet(d)] = d
		}
	}

	return list, nil
}

// ListByService returns pod list from storage by namespace and service names
func (s *PodStorage) ListByService(ctx context.Context, namespace, service string) (map[string]*types.Pod, error) {
	list := make(map[string]*types.Pod, 0)

	prefix := fmt.Sprintf("%s:%s:", namespace, service)

	for _, d := range s.data {
		if strings.HasPrefix(s.keyGet(d), prefix) {
			list[s.keyGet(d)] = d
		}
	}

	return list, nil
}

// ListByDeployment returns pod list from storage by namespace, service and deployment names
func (s *PodStorage) ListByDeployment(ctx context.Context, namespace, service, deployment string) (map[string]*types.Pod, error) {
	list := make(map[string]*types.Pod, 0)

	prefix := fmt.Sprintf("%s:%s:%s:", namespace, service, deployment)

	for _, d := range s.data {
		if strings.HasPrefix(s.keyGet(d), prefix) {
			list[s.keyGet(d)] = d
		}
	}

	return list, nil
}

// Update pod spec
func (s *PodStorage) SetSpec(ctx context.Context, pod *types.Pod) error {
	if err := s.checkPodExists(pod); err != nil {
		return err
	}

	s.data[s.keyGet(pod)].Spec = pod.Spec
	return nil
}

// Update pod status
func (s *PodStorage) SetStatus(ctx context.Context, pod *types.Pod) error {
	if err := s.checkPodExists(pod); err != nil {
		return err
	}

	s.data[s.keyGet(pod)].Status = pod.Status
	return nil
}

// Insert new pod into storage
func (s *PodStorage) Insert(ctx context.Context, pod *types.Pod) error {
	if err := s.checkPodArgument(pod); err != nil {
		return err
	}

	s.data[s.keyGet(pod)] = pod
	return nil
}

// Update pod in storage
func (s *PodStorage) Update(ctx context.Context, pod *types.Pod) error {

	if err := s.checkPodExists(pod); err != nil {
		return err
	}

	s.data[s.keyGet(pod)] = pod

	return nil
}

// Remove pod from storage
func (s *PodStorage) Destroy(ctx context.Context, pod *types.Pod) error {

	if err := s.checkPodExists(pod); err != nil {
		return err
	}

	delete(s.data, s.keyGet(pod))

	return nil
}

// Remove pod from storage
func (s *PodStorage) Remove(ctx context.Context, pod *types.Pod) error {

	if err := s.checkPodExists(pod); err != nil {
		return err
	}

	delete(s.data, s.keyGet(pod))

	return nil
}

// Watch pod changes
func (s *PodStorage) Watch(ctx context.Context, pod chan *types.Pod) error {
	return nil
}

// Watch pod spec changes
func (s *PodStorage) WatchSpec(ctx context.Context, pod chan *types.Pod) error {
	return nil
}

// Watch pod status changes
func (s *PodStorage) WatchStatus(ctx context.Context, pod chan *types.Pod) error {
	return nil
}

// Clear pod storage
func (s *PodStorage) Clear(ctx context.Context) error {
	s.data = make(map[string]*types.Pod)
	return nil
}

// keyCreate util function
func (s *PodStorage) keyCreate(namespace, service, deployment, name string) string {
	return fmt.Sprintf("%s:%s:%s:%s", namespace, service, deployment, name)
}

// keyGet util function
func (s *PodStorage) keyGet(p *types.Pod) string {
	return p.SelfLink()
}

// newPodStorage returns new podStorage
func newPodStorage() *PodStorage {
	s := new(PodStorage)
	s.data = make(map[string]*types.Pod)
	return s
}

// checkPodArgument method checks pod argument
func (s *PodStorage) checkPodArgument(pod *types.Pod) error {
	if pod == nil {
		return errors.New(store.ErrStructArgIsNil)
	}

	if pod.Meta.Name == "" {
		return errors.New(store.ErrStructArgIsInvalid)
	}

	return nil
}

// checkPodArgument method checks if pod exists in storage
func (s *PodStorage) checkPodExists(pod *types.Pod) error {

	if err := s.checkPodArgument(pod); err != nil {
		return err
	}

	if _, ok := s.data[s.keyGet(pod)]; !ok {
		return errors.New(store.ErrEntityNotFound)
	}

	return nil
}
<commit_msg>fix mock database methods<commit_after>//
// Last.Backend LLC CONFIDENTIAL
// __________________
//
// [2014] - [2018] Last.Backend LLC
// All Rights Reserved.
//
// NOTICE:  All information contained herein is, and remains
// the property of Last.Backend LLC and its suppliers,
// if any.  The intellectual and technical concepts contained
// herein are proprietary to Last.Backend LLC
// and its suppliers and may be covered by Russian Federation and Foreign Patents,
// patents in process, and are protected by trade secret or copyright law.
// Dissemination of this information or reproduction of this material
// is strictly forbidden unless prior written permission is obtained
// from Last.Backend LLC.
//

package mock

import (
	"context"
	"fmt"
	"github.com/lastbackend/lastbackend/pkg/distribution/errors"
	"github.com/lastbackend/lastbackend/pkg/distribution/types"
	"github.com/lastbackend/lastbackend/pkg/storage/storage"
	"github.com/lastbackend/lastbackend/pkg/storage/store"
	"strings"
)

// Pod Service type for interface in interfaces folder
type PodStorage struct {
	storage.Pod
	data map[string]*types.Pod
}

// Get pod from storage
func (s *PodStorage) Get(ctx context.Context, namespace, service, deployment, name string) (*types.Pod, error) {
	if ns, ok := s.data[s.keyCreate(namespace, service, deployment, name)]; ok {
		return ns, nil
	}
	return nil, errors.New(store.ErrEntityNotFound)
}

// ListByNamespace returns pod list from storage by namespace
func (s *PodStorage) ListByNamespace(ctx context.Context, namespace string) (map[string]*types.Pod, error) {
	list := make(map[string]*types.Pod, 0)

	prefix := fmt.Sprintf("%s:", namespace)
	for _, d := range s.data {

		if strings.HasPrefix(s.keyGet(d), prefix) {
			list[s.keyGet(d)] = d
		}
	}

	return list, nil
}

// ListByService returns pod list from storage by namespace and service names
func (s *PodStorage) ListByService(ctx context.Context, namespace, service string) (map[string]*types.Pod, error) {
	list := make(map[string]*types.Pod, 0)

	prefix := fmt.Sprintf("%s:%s:", namespace, service)

	for _, d := range s.data {
		if strings.HasPrefix(s.keyGet(d), prefix) {
			list[s.keyGet(d)] = d
		}
	}

	return list, nil
}

// ListByDeployment returns pod list from storage by namespace, service and deployment names
func (s *PodStorage) ListByDeployment(ctx context.Context, namespace, service, deployment string) (map[string]*types.Pod, error) {
	list := make(map[string]*types.Pod, 0)

	prefix := fmt.Sprintf("%s:%s:%s:", namespace, service, deployment)

	for _, d := range s.data {
		if strings.HasPrefix(s.keyGet(d), prefix) {
			list[s.keyGet(d)] = d
		}
	}

	return list, nil
}

// Update pod meta
func (s *PodStorage) SetMeta(ctx context.Context, pod *types.Pod) error {

	if err := s.checkPodExists(pod); err != nil {
		return err
	}

	s.data[s.keyGet(pod)].Meta = pod.Meta

	return nil
}

// Update pod spec
func (s *PodStorage) SetSpec(ctx context.Context, pod *types.Pod) error {
	if err := s.checkPodExists(pod); err != nil {
		return err
	}

	s.data[s.keyGet(pod)].Spec = pod.Spec
	return nil
}

// Update pod status
func (s *PodStorage) SetStatus(ctx context.Context, pod *types.Pod) error {
	if err := s.checkPodExists(pod); err != nil {
		return err
	}

	s.data[s.keyGet(pod)].Status = pod.Status
	return nil
}

// Insert new pod into storage
func (s *PodStorage) Insert(ctx context.Context, pod *types.Pod) error {
	if err := s.checkPodArgument(pod); err != nil {
		return err
	}

	s.data[s.keyGet(pod)] = pod
	return nil
}

// Update pod in storage
func (s *PodStorage) Update(ctx context.Context, pod *types.Pod) error {

	if err := s.checkPodExists(pod); err != nil {
		return err
	}

	s.data[s.keyGet(pod)] = pod

	return nil
}

// Remove pod from storage
func (s *PodStorage) Destroy(ctx context.Context, pod *types.Pod) error {

	if err := s.checkPodExists(pod); err != nil {
		return err
	}

	delete(s.data, s.keyGet(pod))

	return nil
}

// Remove pod from storage
func (s *PodStorage) Remove(ctx context.Context, pod *types.Pod) error {

	if err := s.checkPodExists(pod); err != nil {
		return err
	}

	delete(s.data, s.keyGet(pod))

	return nil
}

// Watch pod changes
func (s *PodStorage) Watch(ctx context.Context, pod chan *types.Pod) error {
	return nil
}

// Watch pod spec changes
func (s *PodStorage) WatchSpec(ctx context.Context, pod chan *types.Pod) error {
	return nil
}

// Watch pod status changes
func (s *PodStorage) WatchStatus(ctx context.Context, pod chan *types.Pod) error {
	return nil
}

// Clear pod storage
func (s *PodStorage) Clear(ctx context.Context) error {
	s.data = make(map[string]*types.Pod)
	return nil
}

// keyCreate util function
func (s *PodStorage) keyCreate(namespace, service, deployment, name string) string {
	return fmt.Sprintf("%s:%s:%s:%s", namespace, service, deployment, name)
}

// keyGet util function
func (s *PodStorage) keyGet(p *types.Pod) string {
	return p.SelfLink()
}

// newPodStorage returns new podStorage
func newPodStorage() *PodStorage {
	s := new(PodStorage)
	s.data = make(map[string]*types.Pod)
	return s
}

// checkPodArgument method checks pod argument
func (s *PodStorage) checkPodArgument(pod *types.Pod) error {
	if pod == nil {
		return errors.New(store.ErrStructArgIsNil)
	}

	if pod.Meta.Name == "" {
		return errors.New(store.ErrStructArgIsInvalid)
	}

	return nil
}

// checkPodArgument method checks if pod exists in storage
func (s *PodStorage) checkPodExists(pod *types.Pod) error {

	if err := s.checkPodArgument(pod); err != nil {
		return err
	}

	if _, ok := s.data[s.keyGet(pod)]; !ok {
		return errors.New(store.ErrEntityNotFound)
	}

	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2020 The cert-manager 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 acme

import (
	"context"
	"fmt"
	"testing"
	"time"

	acmeapi "golang.org/x/crypto/acme"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/utils/clock"

	accountstest "github.com/jetstack/cert-manager/pkg/acme/accounts/test"
	acmecl "github.com/jetstack/cert-manager/pkg/acme/client"
	cmacme "github.com/jetstack/cert-manager/pkg/apis/acme/v1"
	cmmeta "github.com/jetstack/cert-manager/pkg/apis/meta/v1"
	controllerpkg "github.com/jetstack/cert-manager/pkg/controller"
	"github.com/jetstack/cert-manager/pkg/controller/acmeorders"
	logf "github.com/jetstack/cert-manager/pkg/logs"
	"github.com/jetstack/cert-manager/pkg/metrics"
	"github.com/jetstack/cert-manager/test/integration/framework"
	"github.com/jetstack/cert-manager/test/unit/gen"
)

func TestAcmeOrdersController(t *testing.T) {

	config, stopFn := framework.RunControlPlane(t)
	defer stopFn()

	// Create clients and informer factories for Kubernetes API and
	// cert-manager.
	kubeClient, factory, cmCl, cmFactory := framework.NewClients(t, config)

	// some test values
	var (
		testName      string = "acmetest"
		challengeType string = "dns-01"
		authType      string = "dns"
	)

	// Initial ACME authorization to be returned by GetAuthorization.
	auth := &acmeapi.Authorization{
		URI:    testName,
		Status: acmeapi.StatusPending,
		Challenges: []*acmeapi.Challenge{
			{
				Type:  challengeType,
				URI:   testName,
				Token: testName,
			},
		},
		Identifier: acmeapi.AuthzID{
			Type:  authType,
			Value: testName,
		},
	}
	// ACME order to be returned by calls to the fake registry.
	acmeOrder := &acmeapi.Order{
		URI: testName,
		Identifiers: []acmeapi.AuthzID{
			{
				Type:  authType,
				Value: testName,
			},
		},
		FinalizeURL: testName,
		AuthzURLs:   []string{testName},
		Status:      acmeapi.StatusPending,
	}
	// ACME client with stubbed methods to simulate a specific response from the
	// ACME server.
	acmeClient := &acmecl.FakeACME{
		FakeAuthorizeOrder: func(_ context.Context, _ []acmeapi.AuthzID, _ ...acmeapi.OrderOption) (*acmeapi.Order, error) {
			return acmeOrder, nil
		},
		FakeGetAuthorization: func(_ context.Context, _ string) (*acmeapi.Authorization, error) {
			return auth, nil
		},
		FakeGetOrder: func(_ context.Context, _ string) (*acmeapi.Order, error) {
			return acmeOrder, nil
		},
		FakeDNS01ChallengeRecord: func(_ string) (string, error) {
			return testName, nil
		},
		FakeCreateOrderCert: func(_ context.Context, _ string, _ []byte, _ bool) ([][]byte, string, error) {
			// A hack to ensure the status of the _ACME_ order gets set to valid
			// when we're finalizing the order.
			acmeOrder.Status = acmeapi.StatusValid
			return [][]byte{}, "", nil
		},
	}

	// Create a fake ACME registry with a GetClientFunc that returns the
	// acmeClient with the stubbed methods.
	accountRegistry := &accountstest.FakeRegistry{
		GetClientFunc: func(_ string) (acmecl.Interface, error) {
			return acmeClient, nil
		},
	}

	// Create a new orders controller.
	ctrl, queue, mustSync := acmeorders.NewController(
		logf.Log,
		cmCl,
		factory,
		cmFactory,
		accountRegistry,
		framework.NewEventRecorder(t),
		clock.RealClock{},
		false,
	)
	c := controllerpkg.NewController(
		context.Background(),
		"orders_test",
		metrics.New(logf.Log, clock.RealClock{}),
		ctrl.ProcessItem,
		mustSync,
		nil,
		queue,
	)

	// Ensure the controller is started now and stopped after the tests.
	stopController := framework.StartInformersAndController(
		t,
		factory,
		cmFactory,
		c,
	)
	defer stopController()

	ctx, cancel := context.WithTimeout(context.TODO(), time.Second*20)
	defer cancel()

	// Create a Namespace.
	ns := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: testName}}
	_, err := kubeClient.CoreV1().Namespaces().Create(ctx, ns, metav1.CreateOptions{})
	if err != nil {
		t.Fatal(err)
	}

	// Create an Issuer for Order.
	acmeIssuer := cmacme.ACMEIssuer{
		PrivateKey: cmmeta.SecretKeySelector{Key: testName},
		Server:     testName,
		Solvers: []cmacme.ACMEChallengeSolver{
			{
				DNS01: &cmacme.ACMEChallengeSolverDNS01{
					Cloudflare: &cmacme.ACMEIssuerDNS01ProviderCloudflare{
						Email:    testName,
						APIToken: &cmmeta.SecretKeySelector{Key: testName},
					},
				},
			},
		},
	}
	acmeIssuer.PrivateKey.Name = testName
	acmeIssuer.Solvers[0].DNS01.Cloudflare.APIToken.Name = testName
	iss := gen.Issuer(testName,
		gen.SetIssuerNamespace(testName),
		gen.SetIssuerACME(acmeIssuer))

	iss, err = cmCl.CertmanagerV1().Issuers(testName).Create(ctx, iss, metav1.CreateOptions{})
	if err != nil {
		t.Fatal(err)
	}

	// 1. Test that a Challenge is created for a new Order.

	// Create an Order CR.
	order := gen.Order(testName,
		gen.SetOrderIssuer(cmmeta.ObjectReference{
			Name: testName,
		}),
		gen.SetOrderNamespace(testName),
		gen.SetOrderCsr([]byte(testName)),
		gen.SetOrderDNSNames(testName))

	order, err = cmCl.AcmeV1().Orders(testName).Create(ctx, order, metav1.CreateOptions{})
	if err != nil {
		t.Fatal(err)
	}

	// Wait for the Challenge to be created.
	var chal *cmacme.Challenge
	err = wait.Poll(time.Millisecond*100, time.Minute, func() (done bool, err error) {
		chals, err := cmCl.AcmeV1().Challenges(testName).List(ctx, metav1.ListOptions{})
		l := len(chals.Items)
		// Challenge has not been created yet
		if l == 0 {
			return false, nil
		}
		// this should never happen
		if l > 1 {
			return false, fmt.Errorf("expected maximum 1 challenge, got %d", l)
		}
		// Check that the Challenge is owned by our Order.
		chal = &chals.Items[0]
		if !metav1.IsControlledBy(chal, order) {
			return false, fmt.Errorf("found an unexpected Challenge resource: %v", chal.Name)
		}
		return true, nil
	})
	if err != nil {
		t.Fatal(err)
	}

	// 2. Test that in an edge case, where an ACME server is misbehaving and
	// despite Challenges being valid, the ACME order status is 'pending', we
	// re-queue the Order so that when the ACME order does become 'ready', we
	// finalize our Order and, in the success scenario, it eventually becomes
	// valid.
	// https://github.com/jetstack/cert-manager/issues/2868

	// Set the Challenge state to valid- the status of the ACME order remains 'pending'.
	chal = chal.DeepCopy()
	chal.Status.State = cmacme.Valid
	_, err = cmCl.AcmeV1().Challenges(testName).UpdateStatus(ctx, chal, metav1.UpdateOptions{})
	if err != nil {
		t.Fatal(err)
	}

	// Override the default requeue period.
	acmeorders.RequeuePeriod = time.Second * 2

	// Sit here for a little bit checking that the Order status remains pending
	// and also to verify that this test works.
	// TODO: instead of waiting for the Order to remain 'pending', we should use
	// Reason field on Order's status. Change this test once we are setting
	// Reasons on intermittent Order states.
	var pendingOrder *cmacme.Order
	err = wait.Poll(time.Millisecond*200, acmeorders.RequeuePeriod, func() (bool, error) {
		pendingOrder, err = cmCl.AcmeV1().Orders(testName).Get(ctx, testName, metav1.GetOptions{})
		if err != nil {
			return false, err
		}
		if pendingOrder.Status.State != cmacme.Pending {
			return true, nil
		}
		return false, nil
	})
	switch {
	case err == nil:
		t.Fatalf("Expected Order to have pending status instead got: %v", pendingOrder.Status.State)
	case err == wait.ErrWaitTimeout:
		// 'happy case' - Order remained pending
	default:
		t.Fatal(err)
	}

	// Set status of the ACME order to 'ready'.
	acmeOrder.Status = acmeapi.StatusReady

	// Wait for the status of the Order to become Valid.
	err = wait.Poll(time.Millisecond*100, acmeorders.RequeuePeriod*3, func() (bool, error) {
		o, err := cmCl.AcmeV1().Orders(testName).Get(ctx, testName, metav1.GetOptions{})
		if err != nil {
			return false, err
		}
		// not valid yet
		if o.Status.State != cmacme.Valid {
			return false, nil
		}
		return true, nil
	})
	if err != nil {
		t.Fatal(err)
	}
}
<commit_msg>test/integration/acme: fix dropped error<commit_after>/*
Copyright 2020 The cert-manager 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 acme

import (
	"context"
	"fmt"
	"testing"
	"time"

	acmeapi "golang.org/x/crypto/acme"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/utils/clock"

	accountstest "github.com/jetstack/cert-manager/pkg/acme/accounts/test"
	acmecl "github.com/jetstack/cert-manager/pkg/acme/client"
	cmacme "github.com/jetstack/cert-manager/pkg/apis/acme/v1"
	cmmeta "github.com/jetstack/cert-manager/pkg/apis/meta/v1"
	controllerpkg "github.com/jetstack/cert-manager/pkg/controller"
	"github.com/jetstack/cert-manager/pkg/controller/acmeorders"
	logf "github.com/jetstack/cert-manager/pkg/logs"
	"github.com/jetstack/cert-manager/pkg/metrics"
	"github.com/jetstack/cert-manager/test/integration/framework"
	"github.com/jetstack/cert-manager/test/unit/gen"
)

func TestAcmeOrdersController(t *testing.T) {

	config, stopFn := framework.RunControlPlane(t)
	defer stopFn()

	// Create clients and informer factories for Kubernetes API and
	// cert-manager.
	kubeClient, factory, cmCl, cmFactory := framework.NewClients(t, config)

	// some test values
	var (
		testName      string = "acmetest"
		challengeType string = "dns-01"
		authType      string = "dns"
	)

	// Initial ACME authorization to be returned by GetAuthorization.
	auth := &acmeapi.Authorization{
		URI:    testName,
		Status: acmeapi.StatusPending,
		Challenges: []*acmeapi.Challenge{
			{
				Type:  challengeType,
				URI:   testName,
				Token: testName,
			},
		},
		Identifier: acmeapi.AuthzID{
			Type:  authType,
			Value: testName,
		},
	}
	// ACME order to be returned by calls to the fake registry.
	acmeOrder := &acmeapi.Order{
		URI: testName,
		Identifiers: []acmeapi.AuthzID{
			{
				Type:  authType,
				Value: testName,
			},
		},
		FinalizeURL: testName,
		AuthzURLs:   []string{testName},
		Status:      acmeapi.StatusPending,
	}
	// ACME client with stubbed methods to simulate a specific response from the
	// ACME server.
	acmeClient := &acmecl.FakeACME{
		FakeAuthorizeOrder: func(_ context.Context, _ []acmeapi.AuthzID, _ ...acmeapi.OrderOption) (*acmeapi.Order, error) {
			return acmeOrder, nil
		},
		FakeGetAuthorization: func(_ context.Context, _ string) (*acmeapi.Authorization, error) {
			return auth, nil
		},
		FakeGetOrder: func(_ context.Context, _ string) (*acmeapi.Order, error) {
			return acmeOrder, nil
		},
		FakeDNS01ChallengeRecord: func(_ string) (string, error) {
			return testName, nil
		},
		FakeCreateOrderCert: func(_ context.Context, _ string, _ []byte, _ bool) ([][]byte, string, error) {
			// A hack to ensure the status of the _ACME_ order gets set to valid
			// when we're finalizing the order.
			acmeOrder.Status = acmeapi.StatusValid
			return [][]byte{}, "", nil
		},
	}

	// Create a fake ACME registry with a GetClientFunc that returns the
	// acmeClient with the stubbed methods.
	accountRegistry := &accountstest.FakeRegistry{
		GetClientFunc: func(_ string) (acmecl.Interface, error) {
			return acmeClient, nil
		},
	}

	// Create a new orders controller.
	ctrl, queue, mustSync := acmeorders.NewController(
		logf.Log,
		cmCl,
		factory,
		cmFactory,
		accountRegistry,
		framework.NewEventRecorder(t),
		clock.RealClock{},
		false,
	)
	c := controllerpkg.NewController(
		context.Background(),
		"orders_test",
		metrics.New(logf.Log, clock.RealClock{}),
		ctrl.ProcessItem,
		mustSync,
		nil,
		queue,
	)

	// Ensure the controller is started now and stopped after the tests.
	stopController := framework.StartInformersAndController(
		t,
		factory,
		cmFactory,
		c,
	)
	defer stopController()

	ctx, cancel := context.WithTimeout(context.TODO(), time.Second*20)
	defer cancel()

	// Create a Namespace.
	ns := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: testName}}
	_, err := kubeClient.CoreV1().Namespaces().Create(ctx, ns, metav1.CreateOptions{})
	if err != nil {
		t.Fatal(err)
	}

	// Create an Issuer for Order.
	acmeIssuer := cmacme.ACMEIssuer{
		PrivateKey: cmmeta.SecretKeySelector{Key: testName},
		Server:     testName,
		Solvers: []cmacme.ACMEChallengeSolver{
			{
				DNS01: &cmacme.ACMEChallengeSolverDNS01{
					Cloudflare: &cmacme.ACMEIssuerDNS01ProviderCloudflare{
						Email:    testName,
						APIToken: &cmmeta.SecretKeySelector{Key: testName},
					},
				},
			},
		},
	}
	acmeIssuer.PrivateKey.Name = testName
	acmeIssuer.Solvers[0].DNS01.Cloudflare.APIToken.Name = testName
	iss := gen.Issuer(testName,
		gen.SetIssuerNamespace(testName),
		gen.SetIssuerACME(acmeIssuer))

	iss, err = cmCl.CertmanagerV1().Issuers(testName).Create(ctx, iss, metav1.CreateOptions{})
	if err != nil {
		t.Fatal(err)
	}

	// 1. Test that a Challenge is created for a new Order.

	// Create an Order CR.
	order := gen.Order(testName,
		gen.SetOrderIssuer(cmmeta.ObjectReference{
			Name: testName,
		}),
		gen.SetOrderNamespace(testName),
		gen.SetOrderCsr([]byte(testName)),
		gen.SetOrderDNSNames(testName))

	order, err = cmCl.AcmeV1().Orders(testName).Create(ctx, order, metav1.CreateOptions{})
	if err != nil {
		t.Fatal(err)
	}

	// Wait for the Challenge to be created.
	var chal *cmacme.Challenge
	err = wait.Poll(time.Millisecond*100, time.Minute, func() (done bool, err error) {
		chals, err := cmCl.AcmeV1().Challenges(testName).List(ctx, metav1.ListOptions{})
		if err != nil {
			return false, err
		}
		l := len(chals.Items)
		// Challenge has not been created yet
		if l == 0 {
			return false, nil
		}
		// this should never happen
		if l > 1 {
			return false, fmt.Errorf("expected maximum 1 challenge, got %d", l)
		}
		// Check that the Challenge is owned by our Order.
		chal = &chals.Items[0]
		if !metav1.IsControlledBy(chal, order) {
			return false, fmt.Errorf("found an unexpected Challenge resource: %v", chal.Name)
		}
		return true, nil
	})
	if err != nil {
		t.Fatal(err)
	}

	// 2. Test that in an edge case, where an ACME server is misbehaving and
	// despite Challenges being valid, the ACME order status is 'pending', we
	// re-queue the Order so that when the ACME order does become 'ready', we
	// finalize our Order and, in the success scenario, it eventually becomes
	// valid.
	// https://github.com/jetstack/cert-manager/issues/2868

	// Set the Challenge state to valid- the status of the ACME order remains 'pending'.
	chal = chal.DeepCopy()
	chal.Status.State = cmacme.Valid
	_, err = cmCl.AcmeV1().Challenges(testName).UpdateStatus(ctx, chal, metav1.UpdateOptions{})
	if err != nil {
		t.Fatal(err)
	}

	// Override the default requeue period.
	acmeorders.RequeuePeriod = time.Second * 2

	// Sit here for a little bit checking that the Order status remains pending
	// and also to verify that this test works.
	// TODO: instead of waiting for the Order to remain 'pending', we should use
	// Reason field on Order's status. Change this test once we are setting
	// Reasons on intermittent Order states.
	var pendingOrder *cmacme.Order
	err = wait.Poll(time.Millisecond*200, acmeorders.RequeuePeriod, func() (bool, error) {
		pendingOrder, err = cmCl.AcmeV1().Orders(testName).Get(ctx, testName, metav1.GetOptions{})
		if err != nil {
			return false, err
		}
		if pendingOrder.Status.State != cmacme.Pending {
			return true, nil
		}
		return false, nil
	})
	switch {
	case err == nil:
		t.Fatalf("Expected Order to have pending status instead got: %v", pendingOrder.Status.State)
	case err == wait.ErrWaitTimeout:
		// 'happy case' - Order remained pending
	default:
		t.Fatal(err)
	}

	// Set status of the ACME order to 'ready'.
	acmeOrder.Status = acmeapi.StatusReady

	// Wait for the status of the Order to become Valid.
	err = wait.Poll(time.Millisecond*100, acmeorders.RequeuePeriod*3, func() (bool, error) {
		o, err := cmCl.AcmeV1().Orders(testName).Get(ctx, testName, metav1.GetOptions{})
		if err != nil {
			return false, err
		}
		// not valid yet
		if o.Status.State != cmacme.Valid {
			return false, nil
		}
		return true, nil
	})
	if err != nil {
		t.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package tun

import (
	"context"
	"fmt"
	"net"
	"os"
	"runtime"
	"unsafe"

	"golang.org/x/sys/unix"

	"github.com/datawire/dlib/dexec"
	"github.com/telepresenceio/telepresence/v2/pkg/tun/buffer"
)

const devicePath = "/dev/net/tun"

type Device struct {
	*os.File
	name  string
	index int32
}

func openTun() (*Device, error) {
	// https://www.kernel.org/doc/html/latest/networking/tuntap.html

	fd, err := unix.Open(devicePath, unix.O_RDWR, 0)
	if err != nil {
		return nil, fmt.Errorf("failed to open TUN device %s: %v", devicePath, err)
	}
	unix.CloseOnExec(fd)
	defer func() {
		if err != nil {
			_ = unix.Close(fd)
		}
	}()

	var flagsRequest struct {
		name  [unix.IFNAMSIZ]byte
		flags int16
	}
	copy(flagsRequest.name[:], "tel%d")
	flagsRequest.flags = unix.IFF_TUN | unix.IFF_NO_PI

	err = unix.IoctlSetInt(fd, unix.TUNSETIFF, int(uintptr(unsafe.Pointer(&flagsRequest))))
	if err != nil {
		return nil, err
	}

	var name string
	for i := 0; i < unix.IFNAMSIZ; i++ {
		if flagsRequest.name[i] == 0 {
			name = string(flagsRequest.name[0:i])
			break
		}
	}
	if name == "" {
		name = string(flagsRequest.name[:])
	}

	htons := func(value uint16) uint16 {
		test := uint16(1)
		if (*[2]byte)(unsafe.Pointer(&test))[0] == 1 {
			// this machine is little endian, swap bytes
			value = value&0xff<<8 | value&0xff00>>8
		}
		return value
	}

	// Passing a network ordered short here is required.
	provisioningSocket, err := unix.Socket(unix.AF_PACKET, unix.SOCK_RAW, int(htons(unix.ETH_P_ALL)))
	if err != nil {
		return nil, err
	}
	defer unix.Close(provisioningSocket)

	if err = unix.BindToDevice(provisioningSocket, name); err != nil {
		return nil, err
	}
	flagsRequest.flags |= unix.IFF_UP | unix.IFF_RUNNING
	if err = ioctl(provisioningSocket, unix.SIOCSIFFLAGS, unsafe.Pointer(&flagsRequest)); err != nil {
		return nil, err
	}

	index, err := getInterfaceIndex(provisioningSocket, name)
	if err != nil {
		return nil, err
	}

	// Set non-blocking so that Read() doesn't hang for several seconds when the
	// fd is Closed. Read() will still wait for data to arrive.
	//
	// See: https://github.com/golang/go/issues/30426#issuecomment-470044803
	_ = unix.SetNonblock(fd, true)
	return &Device{File: os.NewFile(uintptr(fd), devicePath), name: name, index: index}, nil
}

func (t *Device) addSubnet(ctx context.Context, subnet *net.IPNet) error {
	return dexec.CommandContext(ctx, "ip", "a", "add", subnet.String(), "dev", t.name).Run()
}

func (t *Device) removeSubnet(ctx context.Context, subnet *net.IPNet) error {
	return dexec.CommandContext(ctx, "ip", "a", "del", subnet.String(), "dev", t.name).Run()
}

// Index returns the index of this device
func (t *Device) Index() int32 {
	return t.index
}

func (t *Device) setMTU(mtu int) error {
	return withSocket(unix.AF_INET, func(fd int) error {
		var mtuRequest struct {
			name [unix.IFNAMSIZ]byte
			mtu  int32
		}
		copy(mtuRequest.name[:], t.name)
		mtuRequest.mtu = int32(mtu)
		err := ioctl(fd, unix.SIOCSIFMTU, unsafe.Pointer(&mtuRequest))
		runtime.KeepAlive(&mtuRequest)
		if err != nil {
			err = fmt.Errorf("set MTU on %s failed: %w", t.name, err)
		}
		return err
	})
}

func (t *Device) readPacket(into *buffer.Data) (int, error) {
	return t.File.Read(into.Raw())
}

func (t *Device) writePacket(from *buffer.Data) (int, error) {
	return t.File.Write(from.Raw())
}

func getInterfaceIndex(fd int, name string) (int32, error) {
	var indexRequest struct {
		name  [unix.IFNAMSIZ]byte
		index int32
	}
	copy(indexRequest.name[:], name)
	if err := ioctl(fd, unix.SIOCGIFINDEX, unsafe.Pointer(&indexRequest)); err != nil {
		return 0, fmt.Errorf("get interface index on %s failed: %w", name, err)
	}
	return indexRequest.index, nil
}
<commit_msg>Misc fixes in Linux OpenTun() method.<commit_after>package tun

import (
	"context"
	"fmt"
	"net"
	"os"
	"runtime"
	"unsafe"

	"golang.org/x/sys/unix"

	"github.com/datawire/dlib/dexec"
	"github.com/telepresenceio/telepresence/v2/pkg/tun/buffer"
)

const devicePath = "/dev/net/tun"

type Device struct {
	*os.File
	name  string
	index int32
}

func openTun() (*Device, error) {
	// https://www.kernel.org/doc/html/latest/networking/tuntap.html

	fd, err := unix.Open(devicePath, unix.O_RDWR, 0)
	if err != nil {
		return nil, fmt.Errorf("failed to open TUN device %s: %v", devicePath, err)
	}
	unix.CloseOnExec(fd)
	defer func() {
		if err != nil {
			_ = unix.Close(fd)
		}
	}()

	var flagsRequest struct {
		name  [unix.IFNAMSIZ]byte
		flags int16
	}
	copy(flagsRequest.name[:], "tel%d")
	flagsRequest.flags = unix.IFF_TUN | unix.IFF_NO_PI

	err = unix.IoctlSetInt(fd, unix.TUNSETIFF, int(uintptr(unsafe.Pointer(&flagsRequest))))
	if err != nil {
		return nil, err
	}

	// Retrieve the name that was generated based on the "tel%d" template. The
	// name is zero terminated.
	var name string
	for i := 0; i < unix.IFNAMSIZ; i++ {
		if flagsRequest.name[i] == 0 {
			name = string(flagsRequest.name[0:i])
			break
		}
	}
	if name == "" {
		name = string(flagsRequest.name[:])
	}

	// Set non-blocking so that ReadPacket() doesn't hang for several seconds when the
	// fd is Closed. ReadPacket() will still wait for data to arrive.
	//
	// See: https://github.com/golang/go/issues/30426#issuecomment-470044803
	_ = unix.SetNonblock(fd, true)

	// Bring the device up. This is how it's done in ifconfig.
	provisioningSocket, err := unix.Socket(unix.AF_PACKET, unix.SOCK_DGRAM, unix.IPPROTO_IP)
	if err != nil {
		return nil, err
	}
	defer unix.Close(provisioningSocket)

	flagsRequest.flags = 0
	if err = ioctl(provisioningSocket, unix.SIOCGIFFLAGS, unsafe.Pointer(&flagsRequest)); err != nil {
		return nil, err
	}

	flagsRequest.flags |= unix.IFF_UP | unix.IFF_RUNNING
	if err = ioctl(provisioningSocket, unix.SIOCSIFFLAGS, unsafe.Pointer(&flagsRequest)); err != nil {
		return nil, err
	}

	index, err := getInterfaceIndex(provisioningSocket, name)
	if err != nil {
		return nil, err
	}
	return &Device{File: os.NewFile(uintptr(fd), devicePath), name: name, index: index}, nil
}

func (t *Device) addSubnet(ctx context.Context, subnet *net.IPNet) error {
	return dexec.CommandContext(ctx, "ip", "a", "add", subnet.String(), "dev", t.name).Run()
}

func (t *Device) removeSubnet(ctx context.Context, subnet *net.IPNet) error {
	return dexec.CommandContext(ctx, "ip", "a", "del", subnet.String(), "dev", t.name).Run()
}

// Index returns the index of this device
func (t *Device) Index() int32 {
	return t.index
}

func (t *Device) setMTU(mtu int) error {
	return withSocket(unix.AF_INET, func(fd int) error {
		var mtuRequest struct {
			name [unix.IFNAMSIZ]byte
			mtu  int32
		}
		copy(mtuRequest.name[:], t.name)
		mtuRequest.mtu = int32(mtu)
		err := ioctl(fd, unix.SIOCSIFMTU, unsafe.Pointer(&mtuRequest))
		runtime.KeepAlive(&mtuRequest)
		if err != nil {
			err = fmt.Errorf("set MTU on %s failed: %w", t.name, err)
		}
		return err
	})
}

func (t *Device) readPacket(into *buffer.Data) (int, error) {
	return t.File.Read(into.Raw())
}

func (t *Device) writePacket(from *buffer.Data) (int, error) {
	return t.File.Write(from.Raw())
}

func getInterfaceIndex(fd int, name string) (int32, error) {
	var indexRequest struct {
		name  [unix.IFNAMSIZ]byte
		index int32
	}
	copy(indexRequest.name[:], name)
	if err := ioctl(fd, unix.SIOCGIFINDEX, unsafe.Pointer(&indexRequest)); err != nil {
		return 0, fmt.Errorf("get interface index on %s failed: %w", name, err)
	}
	return indexRequest.index, nil
}
<|endoftext|>