<commit_before>//  Copyright (c) 2014 Couchbase, Inc.
//
// 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 query

import (
	"encoding/json"
	"fmt"
	"math"
	"time"

	"github.com/blevesearch/bleve/analysis/datetime/optional"
	"github.com/blevesearch/bleve/index"
	"github.com/blevesearch/bleve/mapping"
	"github.com/blevesearch/bleve/numeric"
	"github.com/blevesearch/bleve/registry"
	"github.com/blevesearch/bleve/search"
	"github.com/blevesearch/bleve/search/searcher"
)

// QueryDateTimeParser controls the default query date time parser
var QueryDateTimeParser = optional.Name

// QueryDateTimeFormat controls the format when Marshaling to JSON
var QueryDateTimeFormat = time.RFC3339

var cache = registry.NewCache()

type BleveQueryTime struct {
	time.Time
}

var minValidTime time.Time
var maxValidTime time.Time

func init() {
	minValidTime, _ = time.Parse(time.RFC3339, "1677-12-01T00:00:00Z")
	maxValidTime, _ = time.Parse(time.RFC3339, "2262-04-11T11:59:59Z")
}

func queryTimeFromString(t string) (time.Time, error) {
	dateTimeParser, err := cache.DateTimeParserNamed(QueryDateTimeParser)
	if err != nil {
		return time.Time{}, err
	}
	rv, err := dateTimeParser.ParseDateTime(t)
	if err != nil {
		return time.Time{}, err
	}
	return rv, nil
}

func (t *BleveQueryTime) MarshalJSON() ([]byte, error) {
	tt := time.Time(t.Time)
	return []byte("\"" + tt.Format(QueryDateTimeFormat) + "\""), nil
}

func (t *BleveQueryTime) UnmarshalJSON(data []byte) error {
	var timeString string
	err := json.Unmarshal(data, &timeString)
	if err != nil {
		return err
	}
	dateTimeParser, err := cache.DateTimeParserNamed(QueryDateTimeParser)
	if err != nil {
		return err
	}
	t.Time, err = dateTimeParser.ParseDateTime(timeString)
	if err != nil {
		return err
	}
	return nil
}

type DateRangeQuery struct {
	Start          BleveQueryTime `json:"start,omitempty"`
	End            BleveQueryTime `json:"end,omitempty"`
	InclusiveStart *bool          `json:"inclusive_start,omitempty"`
	InclusiveEnd   *bool          `json:"inclusive_end,omitempty"`
	FieldVal       string         `json:"field,omitempty"`
	BoostVal       *Boost         `json:"boost,omitempty"`
}

// NewDateRangeQuery creates a new Query for ranges
// of date values.
// Date strings are parsed using the DateTimeParser configured in the
//  top-level config.QueryDateTimeParser
// Either, but not both endpoints can be nil.
func NewDateRangeQuery(start, end time.Time) *DateRangeQuery {
	return NewDateRangeInclusiveQuery(start, end, nil, nil)
}

// NewDateRangeInclusiveQuery creates a new Query for ranges
// of date values.
// Date strings are parsed using the DateTimeParser configured in the
//  top-level config.QueryDateTimeParser
// Either, but not both endpoints can be nil.
// startInclusive and endInclusive control inclusion of the endpoints.
func NewDateRangeInclusiveQuery(start, end time.Time, startInclusive, endInclusive *bool) *DateRangeQuery {
	return &DateRangeQuery{
		Start:          BleveQueryTime{start},
		End:            BleveQueryTime{end},
		InclusiveStart: startInclusive,
		InclusiveEnd:   endInclusive,
	}
}

func (q *DateRangeQuery) SetBoost(b float64) {
	boost := Boost(b)
	q.BoostVal = &boost
}

func (q *DateRangeQuery) Boost() float64 {
	return q.BoostVal.Value()
}

func (q *DateRangeQuery) SetField(f string) {
	q.FieldVal = f
}

func (q *DateRangeQuery) Field() string {
	return q.FieldVal
}

func (q *DateRangeQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, options search.SearcherOptions) (search.Searcher, error) {
	min, max, err := q.parseEndpoints()
	if err != nil {
		return nil, err
	}

	field := q.FieldVal
	if q.FieldVal == "" {
		field = m.DefaultSearchField()
	}

	return searcher.NewNumericRangeSearcher(i, min, max, q.InclusiveStart, q.InclusiveEnd, field, q.BoostVal.Value(), options)
}

func (q *DateRangeQuery) parseEndpoints() (*float64, *float64, error) {
	min := math.Inf(-1)
	max := math.Inf(1)
	if !q.Start.IsZero() {
		if q.Start.Before(minValidTime) || q.Start.After(maxValidTime) {
			// overflow
			return nil, nil, fmt.Errorf("invalid/unsupported date range, start: %v", q.Start)
		}
		startInt64 := q.Start.UnixNano()
		min = numeric.Int64ToFloat64(startInt64)
	}
	if !q.End.IsZero() {
		if q.End.Before(minValidTime) || q.End.After(maxValidTime) {
			// overflow
			return nil, nil, fmt.Errorf("invalid/unsupported date range, end: %v", q.End)
		}
		endInt64 := q.End.UnixNano()
		max = numeric.Int64ToFloat64(endInt64)
	}

	return &min, &max, nil
}

func (q *DateRangeQuery) Validate() error {
	if q.Start.IsZero() && q.End.IsZero() {
		return fmt.Errorf("must specify start or end")
	}
	_, _, err := q.parseEndpoints()
	if err != nil {
		return err
	}
	return nil
}
<commit_msg>MB-33600: Checking if datetimes are RFC3339 compatible<commit_after>//  Copyright (c) 2014 Couchbase, Inc.
//
// 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 query

import (
	"encoding/json"
	"fmt"
	"math"
	"time"

	"github.com/blevesearch/bleve/analysis/datetime/optional"
	"github.com/blevesearch/bleve/index"
	"github.com/blevesearch/bleve/mapping"
	"github.com/blevesearch/bleve/numeric"
	"github.com/blevesearch/bleve/registry"
	"github.com/blevesearch/bleve/search"
	"github.com/blevesearch/bleve/search/searcher"
)

// QueryDateTimeParser controls the default query date time parser
var QueryDateTimeParser = optional.Name

// QueryDateTimeFormat controls the format when Marshaling to JSON
var QueryDateTimeFormat = time.RFC3339

var cache = registry.NewCache()

type BleveQueryTime struct {
	time.Time
}

var MinRFC3339CompatibleTime time.Time
var MaxRFC3339CompatibleTime time.Time

func init() {
	MinRFC3339CompatibleTime, _ = time.Parse(time.RFC3339, "1677-12-01T00:00:00Z")
	MaxRFC3339CompatibleTime, _ = time.Parse(time.RFC3339, "2262-04-11T11:59:59Z")
}

func queryTimeFromString(t string) (time.Time, error) {
	dateTimeParser, err := cache.DateTimeParserNamed(QueryDateTimeParser)
	if err != nil {
		return time.Time{}, err
	}
	rv, err := dateTimeParser.ParseDateTime(t)
	if err != nil {
		return time.Time{}, err
	}
	return rv, nil
}

func (t *BleveQueryTime) MarshalJSON() ([]byte, error) {
	tt := time.Time(t.Time)
	return []byte("\"" + tt.Format(QueryDateTimeFormat) + "\""), nil
}

func (t *BleveQueryTime) UnmarshalJSON(data []byte) error {
	var timeString string
	err := json.Unmarshal(data, &timeString)
	if err != nil {
		return err
	}
	dateTimeParser, err := cache.DateTimeParserNamed(QueryDateTimeParser)
	if err != nil {
		return err
	}
	t.Time, err = dateTimeParser.ParseDateTime(timeString)
	if err != nil {
		return err
	}
	return nil
}

type DateRangeQuery struct {
	Start          BleveQueryTime `json:"start,omitempty"`
	End            BleveQueryTime `json:"end,omitempty"`
	InclusiveStart *bool          `json:"inclusive_start,omitempty"`
	InclusiveEnd   *bool          `json:"inclusive_end,omitempty"`
	FieldVal       string         `json:"field,omitempty"`
	BoostVal       *Boost         `json:"boost,omitempty"`
}

// NewDateRangeQuery creates a new Query for ranges
// of date values.
// Date strings are parsed using the DateTimeParser configured in the
//  top-level config.QueryDateTimeParser
// Either, but not both endpoints can be nil.
func NewDateRangeQuery(start, end time.Time) *DateRangeQuery {
	return NewDateRangeInclusiveQuery(start, end, nil, nil)
}

// NewDateRangeInclusiveQuery creates a new Query for ranges
// of date values.
// Date strings are parsed using the DateTimeParser configured in the
//  top-level config.QueryDateTimeParser
// Either, but not both endpoints can be nil.
// startInclusive and endInclusive control inclusion of the endpoints.
func NewDateRangeInclusiveQuery(start, end time.Time, startInclusive, endInclusive *bool) *DateRangeQuery {
	return &DateRangeQuery{
		Start:          BleveQueryTime{start},
		End:            BleveQueryTime{end},
		InclusiveStart: startInclusive,
		InclusiveEnd:   endInclusive,
	}
}

func (q *DateRangeQuery) SetBoost(b float64) {
	boost := Boost(b)
	q.BoostVal = &boost
}

func (q *DateRangeQuery) Boost() float64 {
	return q.BoostVal.Value()
}

func (q *DateRangeQuery) SetField(f string) {
	q.FieldVal = f
}

func (q *DateRangeQuery) Field() string {
	return q.FieldVal
}

func (q *DateRangeQuery) Searcher(i index.IndexReader, m mapping.IndexMapping, options search.SearcherOptions) (search.Searcher, error) {
	min, max, err := q.parseEndpoints()
	if err != nil {
		return nil, err
	}

	field := q.FieldVal
	if q.FieldVal == "" {
		field = m.DefaultSearchField()
	}

	return searcher.NewNumericRangeSearcher(i, min, max, q.InclusiveStart, q.InclusiveEnd, field, q.BoostVal.Value(), options)
}

func (q *DateRangeQuery) parseEndpoints() (*float64, *float64, error) {
	min := math.Inf(-1)
	max := math.Inf(1)
	if !q.Start.IsZero() {
		if !isDatetimeCompatible(q.Start) {
			// overflow
			return nil, nil, fmt.Errorf("invalid/unsupported date range, start: %v", q.Start)
		}
		startInt64 := q.Start.UnixNano()
		min = numeric.Int64ToFloat64(startInt64)
	}
	if !q.End.IsZero() {
		if !isDatetimeCompatible(q.End) {
			// overflow
			return nil, nil, fmt.Errorf("invalid/unsupported date range, end: %v", q.End)
		}
		endInt64 := q.End.UnixNano()
		max = numeric.Int64ToFloat64(endInt64)
	}

	return &min, &max, nil
}

func (q *DateRangeQuery) Validate() error {
	if q.Start.IsZero() && q.End.IsZero() {
		return fmt.Errorf("must specify start or end")
	}
	_, _, err := q.parseEndpoints()
	if err != nil {
		return err
	}
	return nil
}

func isDatetimeCompatible(t BleveQueryTime) bool {
	if QueryDateTimeFormat == time.RFC3339 &&
		(t.Before(MinRFC3339CompatibleTime) || t.After(MaxRFC3339CompatibleTime)) {
		return false
	}

	return true
}
<|endoftext|>


<commit_before>// Copyright 2011 Viktor Kojouharov. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//
// Package renderer provides a helper type for rendering html/template files.
// There is always a 'base' template, which acts as an initial template onto
// which functions may be registered and used in the subsequent template chain.
// Each template chain always ends with the 'base' template, and is cached after
// being parsed.
package renderer

import (
	"errors"
	"fmt"
	"html/template"
	"io"
	"io/ioutil"
	"strings"
	"sync"

	"github.com/urandom/webfw/context"
	"github.com/urandom/webfw/fs"
	"github.com/urandom/webfw/util"
)

type Renderer interface {
	BaseName() string
	SetBaseName(string)
	Funcs(template.FuncMap)
	Delims(left, right string)
	Render(w io.Writer, data RenderData, cdata context.ContextData, names ...string) error
}

type renderer struct {
	mutex      sync.RWMutex
	tmpls      map[string]*template.Template
	path       string
	baseName   string
	leftDelim  string
	rightDelim string
	funcMaps   []template.FuncMap
}

type RenderCtx func(w io.Writer, data RenderData, names ...string) error

type RenderData map[string]interface{}

// NewRenderer creates a new renderer object. The path points to a directory
// containing the template files. The base is the name of the file for the
// 'base' template.
func NewRenderer(path, base string) Renderer {
	return &renderer{
		tmpls:    make(map[string]*template.Template),
		path:     path,
		baseName: base,
	}
}

// BaseName returns the current base template name
func (r *renderer) BaseName() string {
	return r.baseName
}

// SetBaseName sets a new template base name
func (r *renderer) SetBaseName(base string) {
	r.baseName = base
}

// Sets the action delimeters for all future templates
func (r *renderer) Delims(left, right string) {
	r.leftDelim = left
	r.rightDelim = right
}

// Funcs registers a template.FuncMap object all future templates
func (r *renderer) Funcs(funcMap template.FuncMap) {
	r.funcMaps = append(r.funcMaps, funcMap)
}

// Render executes the template chain, writing the output in the given
// io.Writer. The data is the user data, passed to the template upon execution.
// The context data will also be added the the template data. All context data
// using string keys will be added to the template data under the 'ctx' key.
// All framework data will be added to the template data under the 'base' key.
// This data may contain:
//   - "lang", the current language
//   - "langs", all supported languages
//   - "r", the current request
//   - "params", the current route params
//   - "config", the framework configuration
//   - "session", the session
//   - "logger", the error logger
//   - "firstTimer", if the session is newly created
// The list of data is partially dependant on the middleware chain
func (r *renderer) Render(w io.Writer, data RenderData, cdata context.ContextData, names ...string) error {
	var tmpl *template.Template

	if len(names) == 0 {
		if t, err := r.base(); err != nil {
			return err
		} else {
			var err error

			if tmpl, err = t.Clone(); err != nil {
				return err
			}
		}
	} else {
		var ok bool
		key := strings.Join(names, "-")
		tmpl, ok = r.get(key)

		if !ok {
			t, err := r.base()
			if err != nil {
				return err
			}

			t, err = t.Clone()
			if err != nil {
				return err
			}

			t, err = parseFiles(t, r.path, names...)

			if err != nil {
				return err
			}

			r.set(key, t)
			tmpl = t
		}
	}

	buf := util.BufferPool.GetBuffer()

	if data == nil {
		data = RenderData{}
	}

	baseData := RenderData{}

	data["base"] = baseData

	contextData := RenderData{}

	data["ctx"] = contextData

	baseData["template"] = tmpl
	for k, v := range cdata {
		switch t := k.(type) {
		case string:
			contextData[t] = v
		case context.BaseCtxKey:
			baseData[string(t)] = v
		}
	}

	if err := tmpl.Execute(buf, data); err != nil {
		return err
	}

	buf.WriteTo(w)

	util.BufferPool.Put(buf)

	return nil
}

func (r *renderer) get(name string) (*template.Template, bool) {
	r.mutex.RLock()
	defer r.mutex.RUnlock()

	t, ok := r.tmpls[name]

	return t, ok
}

func (r *renderer) set(name string, t *template.Template) {
	r.mutex.Lock()
	defer r.mutex.Unlock()

	r.tmpls[name] = t
}

func (r *renderer) base() (*template.Template, error) {
	r.mutex.RLock()

	if t, ok := r.tmpls[r.baseName]; ok {
		r.mutex.RUnlock()
		return t, nil
	}

	r.mutex.RUnlock()
	return r.create(r.baseName)
}

func (r *renderer) create(name string) (*template.Template, error) {
	r.mutex.Lock()
	defer r.mutex.Unlock()

	t := template.New(name)
	/* Add any .Funcs before calling .Parse */

	if r.leftDelim != "" && r.rightDelim != "" {
		t.Delims(r.leftDelim, r.rightDelim)
	}

	for _, fm := range r.funcMaps {
		t.Funcs(fm)
	}

	if t, err := parseFiles(t, r.path, name); err == nil {
		r.tmpls[name] = t
	} else {
		return nil, err
	}

	return t, nil
}

func parseFiles(t *template.Template, root string, names ...string) (*template.Template, error) {
	for _, name := range names {
		var tmpl *template.Template
		if name == t.Name() {
			tmpl = t
		} else {
			tmpl = t.New(name)
		}

		if file, err := fs.DefaultFS.OpenRoot(root, name); err == nil {
			if b, err := ioutil.ReadAll(file); err == nil {
				tmpl.Parse(string(b))
			} else {
				return nil, errors.New(fmt.Sprintf("Error reading '%s' with root '%s': %v\n", name, root, err))
			}
		} else {
			return nil, errors.New(fmt.Sprintf("Error opening '%s' with root '%s': %v\n", name, root, err))
		}
	}

	return t, nil
}
<commit_msg>Allow template caching to be skipped<commit_after>// Copyright 2011 Viktor Kojouharov. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//
// Package renderer provides a helper type for rendering html/template files.
// There is always a 'base' template, which acts as an initial template onto
// which functions may be registered and used in the subsequent template chain.
// Each template chain always ends with the 'base' template, and is cached after
// being parsed.
package renderer

import (
	"errors"
	"fmt"
	"html/template"
	"io"
	"io/ioutil"
	"strings"
	"sync"

	"github.com/urandom/webfw/context"
	"github.com/urandom/webfw/fs"
	"github.com/urandom/webfw/util"
)

type Renderer interface {
	BaseName() string
	SetBaseName(string)
	Funcs(template.FuncMap)
	Delims(left, right string)
	SkipCache(skip ...bool) bool
	Render(w io.Writer, data RenderData, cdata context.ContextData, names ...string) error
}

type renderer struct {
	mutex      sync.RWMutex
	tmpls      map[string]*template.Template
	path       string
	baseName   string
	leftDelim  string
	rightDelim string
	skipCache  bool
	funcMaps   []template.FuncMap
}

type RenderCtx func(w io.Writer, data RenderData, names ...string) error

type RenderData map[string]interface{}

// NewRenderer creates a new renderer object. The path points to a directory
// containing the template files. The base is the name of the file for the
// 'base' template.
func NewRenderer(path, base string) Renderer {
	return &renderer{
		tmpls:    make(map[string]*template.Template),
		path:     path,
		baseName: base,
	}
}

// BaseName returns the current base template name
func (r *renderer) BaseName() string {
	return r.baseName
}

// SetBaseName sets a new template base name
func (r *renderer) SetBaseName(base string) {
	r.baseName = base
}

// Sets the action delimeters for all future templates
func (r *renderer) Delims(left, right string) {
	r.leftDelim = left
	r.rightDelim = right
}

// Funcs registers a template.FuncMap object all future templates
func (r *renderer) Funcs(funcMap template.FuncMap) {
	r.funcMaps = append(r.funcMaps, funcMap)
}

func (r *renderer) SkipCache(skip ...bool) bool {
	if len(skip) > 0 {
		r.skipCache = skip[0]
	}

	return r.skipCache
}

// Render executes the template chain, writing the output in the given
// io.Writer. The data is the user data, passed to the template upon execution.
// The context data will also be added the the template data. All context data
// using string keys will be added to the template data under the 'ctx' key.
// All framework data will be added to the template data under the 'base' key.
// This data may contain:
//   - "lang", the current language
//   - "langs", all supported languages
//   - "r", the current request
//   - "params", the current route params
//   - "config", the framework configuration
//   - "session", the session
//   - "logger", the error logger
//   - "firstTimer", if the session is newly created
// The list of data is partially dependant on the middleware chain
func (r *renderer) Render(w io.Writer, data RenderData, cdata context.ContextData, names ...string) error {
	var tmpl *template.Template

	if len(names) == 0 {
		if t, err := r.base(); err != nil {
			return err
		} else {
			var err error

			if tmpl, err = t.Clone(); err != nil {
				return err
			}
		}
	} else {
		var ok bool
		key := strings.Join(names, "-")
		tmpl, ok = r.get(key)

		if !ok {
			t, err := r.base()
			if err != nil {
				return err
			}

			t, err = t.Clone()
			if err != nil {
				return err
			}

			t, err = parseFiles(t, r.path, names...)

			if err != nil {
				return err
			}

			r.set(key, t)
			tmpl = t
		}
	}

	buf := util.BufferPool.GetBuffer()

	if data == nil {
		data = RenderData{}
	}

	baseData := RenderData{}

	data["base"] = baseData

	contextData := RenderData{}

	data["ctx"] = contextData

	baseData["template"] = tmpl
	for k, v := range cdata {
		switch t := k.(type) {
		case string:
			contextData[t] = v
		case context.BaseCtxKey:
			baseData[string(t)] = v
		}
	}

	if err := tmpl.Execute(buf, data); err != nil {
		return err
	}

	buf.WriteTo(w)

	util.BufferPool.Put(buf)

	return nil
}

func (r *renderer) get(name string) (*template.Template, bool) {
	if r.skipCache {
		return nil, false
	}

	r.mutex.RLock()
	defer r.mutex.RUnlock()

	t, ok := r.tmpls[name]

	return t, ok
}

func (r *renderer) set(name string, t *template.Template) {
	if r.skipCache {
		return
	}

	r.mutex.Lock()
	defer r.mutex.Unlock()

	r.tmpls[name] = t
}

func (r *renderer) base() (*template.Template, error) {
	if !r.skipCache {
		r.mutex.RLock()

		if t, ok := r.tmpls[r.baseName]; ok {
			r.mutex.RUnlock()
			return t, nil
		}

		r.mutex.RUnlock()
	}
	return r.create(r.baseName)
}

func (r *renderer) create(name string) (*template.Template, error) {
	r.mutex.Lock()
	defer r.mutex.Unlock()

	t := template.New(name)
	/* Add any .Funcs before calling .Parse */

	if r.leftDelim != "" && r.rightDelim != "" {
		t.Delims(r.leftDelim, r.rightDelim)
	}

	for _, fm := range r.funcMaps {
		t.Funcs(fm)
	}

	if t, err := parseFiles(t, r.path, name); err == nil {
		if !r.skipCache {
			r.tmpls[name] = t
		}
	} else {
		return nil, err
	}

	return t, nil
}

func parseFiles(t *template.Template, root string, names ...string) (*template.Template, error) {
	for _, name := range names {
		var tmpl *template.Template
		if name == t.Name() {
			tmpl = t
		} else {
			tmpl = t.New(name)
		}

		if file, err := fs.DefaultFS.OpenRoot(root, name); err == nil {
			if b, err := ioutil.ReadAll(file); err == nil {
				tmpl.Parse(string(b))
			} else {
				return nil, errors.New(fmt.Sprintf("Error reading '%s' with root '%s': %v\n", name, root, err))
			}
		} else {
			return nil, errors.New(fmt.Sprintf("Error opening '%s' with root '%s': %v\n", name, root, err))
		}
	}

	return t, nil
}
<|endoftext|>


<commit_before>/*
Copyright 2014 The Kubernetes Authors.

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

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

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

package serviceaccount

import (
	"fmt"
	"time"

	"github.com/golang/glog"
	"k8s.io/api/core/v1"
	apierrs "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	utilerrors "k8s.io/apimachinery/pkg/util/errors"
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
	"k8s.io/apimachinery/pkg/util/wait"
	coreinformers "k8s.io/client-go/informers/core/v1"
	clientset "k8s.io/client-go/kubernetes"
	corelisters "k8s.io/client-go/listers/core/v1"
	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/util/workqueue"
	"k8s.io/kubernetes/pkg/controller"
	"k8s.io/kubernetes/pkg/util/metrics"
)

// ServiceAccountsControllerOptions contains options for running a ServiceAccountsController
type ServiceAccountsControllerOptions struct {
	// ServiceAccounts is the list of service accounts to ensure exist in every namespace
	ServiceAccounts []v1.ServiceAccount

	// ServiceAccountResync is the interval between full resyncs of ServiceAccounts.
	// If non-zero, all service accounts will be re-listed this often.
	// Otherwise, re-list will be delayed as long as possible (until the watch is closed or times out).
	ServiceAccountResync time.Duration

	// NamespaceResync is the interval between full resyncs of Namespaces.
	// If non-zero, all namespaces will be re-listed this often.
	// Otherwise, re-list will be delayed as long as possible (until the watch is closed or times out).
	NamespaceResync time.Duration
}

func DefaultServiceAccountsControllerOptions() ServiceAccountsControllerOptions {
	return ServiceAccountsControllerOptions{
		ServiceAccounts: []v1.ServiceAccount{
			{ObjectMeta: metav1.ObjectMeta{Name: "default"}},
		},
	}
}

// NewServiceAccountsController returns a new *ServiceAccountsController.
func NewServiceAccountsController(saInformer coreinformers.ServiceAccountInformer, nsInformer coreinformers.NamespaceInformer, cl clientset.Interface, options ServiceAccountsControllerOptions) (*ServiceAccountsController, error) {
	e := &ServiceAccountsController{
		client:                  cl,
		serviceAccountsToEnsure: options.ServiceAccounts,
		queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "serviceaccount"),
	}
	if cl != nil && cl.CoreV1().RESTClient().GetRateLimiter() != nil {
		if err := metrics.RegisterMetricAndTrackRateLimiterUsage("serviceaccount_controller", cl.CoreV1().RESTClient().GetRateLimiter()); err != nil {
			return nil, err
		}
	}

	saInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
		DeleteFunc: e.serviceAccountDeleted,
	})
	e.saLister = saInformer.Lister()
	e.saListerSynced = saInformer.Informer().HasSynced

	nsInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
		AddFunc:    e.namespaceAdded,
		UpdateFunc: e.namespaceUpdated,
	})
	e.nsLister = nsInformer.Lister()
	e.nsListerSynced = nsInformer.Informer().HasSynced

	e.syncHandler = e.syncNamespace

	return e, nil
}

// ServiceAccountsController manages ServiceAccount objects inside Namespaces
type ServiceAccountsController struct {
	client                  clientset.Interface
	serviceAccountsToEnsure []v1.ServiceAccount

	// To allow injection for testing.
	syncHandler func(key string) error

	saLister       corelisters.ServiceAccountLister
	saListerSynced cache.InformerSynced

	nsLister       corelisters.NamespaceLister
	nsListerSynced cache.InformerSynced

	queue workqueue.RateLimitingInterface
}

func (c *ServiceAccountsController) Run(workers int, stopCh <-chan struct{}) {
	defer utilruntime.HandleCrash()
	defer c.queue.ShutDown()

	glog.Infof("Starting service account controller")
	defer glog.Infof("Shutting down service account controller")

	if !controller.WaitForCacheSync("service account", stopCh, c.saListerSynced, c.nsListerSynced) {
		return
	}

	for i := 0; i < workers; i++ {
		go wait.Until(c.runWorker, time.Second, stopCh)
	}

	<-stopCh
}

// serviceAccountDeleted reacts to a ServiceAccount deletion by recreating a default ServiceAccount in the namespace if needed
func (c *ServiceAccountsController) serviceAccountDeleted(obj interface{}) {
	sa, ok := obj.(*v1.ServiceAccount)
	if !ok {
		tombstone, ok := obj.(cache.DeletedFinalStateUnknown)
		if !ok {
			utilruntime.HandleError(fmt.Errorf("Couldn't get object from tombstone %#v", obj))
			return
		}
		sa, ok = tombstone.Obj.(*v1.ServiceAccount)
		if !ok {
			utilruntime.HandleError(fmt.Errorf("Tombstone contained object that is not a ServiceAccount %#v", obj))
			return
		}
	}
	c.queue.Add(sa.Namespace)
}

// namespaceAdded reacts to a Namespace creation by creating a default ServiceAccount object
func (c *ServiceAccountsController) namespaceAdded(obj interface{}) {
	namespace := obj.(*v1.Namespace)
	c.queue.Add(namespace.Name)
}

// namespaceUpdated reacts to a Namespace update (or re-list) by creating a default ServiceAccount in the namespace if needed
func (c *ServiceAccountsController) namespaceUpdated(oldObj interface{}, newObj interface{}) {
	newNamespace := newObj.(*v1.Namespace)
	c.queue.Add(newNamespace.Name)
}

func (c *ServiceAccountsController) runWorker() {
	for c.processNextWorkItem() {
	}
}

// processNextWorkItem deals with one key off the queue.  It returns false when it's time to quit.
func (c *ServiceAccountsController) processNextWorkItem() bool {
	key, quit := c.queue.Get()
	if quit {
		return false
	}
	defer c.queue.Done(key)

	err := c.syncHandler(key.(string))
	if err == nil {
		c.queue.Forget(key)
		return true
	}

	utilruntime.HandleError(fmt.Errorf("%v failed with : %v", key, err))
	c.queue.AddRateLimited(key)

	return true
}
func (c *ServiceAccountsController) syncNamespace(key string) error {
	startTime := time.Now()
	defer func() {
		glog.V(4).Infof("Finished syncing namespace %q (%v)", key, time.Now().Sub(startTime))
	}()

	ns, err := c.nsLister.Get(key)
	if apierrs.IsNotFound(err) {
		return nil
	}
	if err != nil {
		return err
	}
	if ns.Status.Phase != v1.NamespaceActive {
		// If namespace is not active, we shouldn't try to create anything
		return nil
	}

	createFailures := []error{}
	for i := range c.serviceAccountsToEnsure {
		sa := c.serviceAccountsToEnsure[i]
		switch _, err := c.saLister.ServiceAccounts(ns.Name).Get(sa.Name); {
		case err == nil:
			continue
		case apierrs.IsNotFound(err):
		case err != nil:
			return err
		}
		// this is only safe because we never read it and we always write it
		// TODO eliminate this once the fake client can handle creation without NS
		sa.Namespace = ns.Name

		if _, err := c.client.CoreV1().ServiceAccounts(ns.Name).Create(&sa); err != nil && !apierrs.IsAlreadyExists(err) {
			createFailures = append(createFailures, err)
		}
	}

	return utilerrors.Flatten(utilerrors.NewAggregate(createFailures))
}
<commit_msg>UPSTREAM: <drop>: skip controller metric error, drop once we run in a separate process<commit_after>/*
Copyright 2014 The Kubernetes Authors.

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

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

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

package serviceaccount

import (
	"fmt"
	"time"

	"github.com/golang/glog"
	"k8s.io/api/core/v1"
	apierrs "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	utilerrors "k8s.io/apimachinery/pkg/util/errors"
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
	"k8s.io/apimachinery/pkg/util/wait"
	coreinformers "k8s.io/client-go/informers/core/v1"
	clientset "k8s.io/client-go/kubernetes"
	corelisters "k8s.io/client-go/listers/core/v1"
	"k8s.io/client-go/tools/cache"
	"k8s.io/client-go/util/workqueue"
	"k8s.io/kubernetes/pkg/controller"
	"k8s.io/kubernetes/pkg/util/metrics"
)

// ServiceAccountsControllerOptions contains options for running a ServiceAccountsController
type ServiceAccountsControllerOptions struct {
	// ServiceAccounts is the list of service accounts to ensure exist in every namespace
	ServiceAccounts []v1.ServiceAccount

	// ServiceAccountResync is the interval between full resyncs of ServiceAccounts.
	// If non-zero, all service accounts will be re-listed this often.
	// Otherwise, re-list will be delayed as long as possible (until the watch is closed or times out).
	ServiceAccountResync time.Duration

	// NamespaceResync is the interval between full resyncs of Namespaces.
	// If non-zero, all namespaces will be re-listed this often.
	// Otherwise, re-list will be delayed as long as possible (until the watch is closed or times out).
	NamespaceResync time.Duration
}

func DefaultServiceAccountsControllerOptions() ServiceAccountsControllerOptions {
	return ServiceAccountsControllerOptions{
		ServiceAccounts: []v1.ServiceAccount{
			{ObjectMeta: metav1.ObjectMeta{Name: "default"}},
		},
	}
}

// NewServiceAccountsController returns a new *ServiceAccountsController.
func NewServiceAccountsController(saInformer coreinformers.ServiceAccountInformer, nsInformer coreinformers.NamespaceInformer, cl clientset.Interface, options ServiceAccountsControllerOptions) (*ServiceAccountsController, error) {
	e := &ServiceAccountsController{
		client:                  cl,
		serviceAccountsToEnsure: options.ServiceAccounts,
		queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "serviceaccount"),
	}
	if cl != nil && cl.CoreV1().RESTClient().GetRateLimiter() != nil {
		// skip the error.  We run two of these in the same process until we split
		metrics.RegisterMetricAndTrackRateLimiterUsage("serviceaccount_controller", cl.CoreV1().RESTClient().GetRateLimiter())
		//if err := metrics.RegisterMetricAndTrackRateLimiterUsage("serviceaccount_controller", cl.CoreV1().RESTClient().GetRateLimiter()); err != nil {
		//	return nil, err
		//}
	}

	saInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
		DeleteFunc: e.serviceAccountDeleted,
	})
	e.saLister = saInformer.Lister()
	e.saListerSynced = saInformer.Informer().HasSynced

	nsInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
		AddFunc:    e.namespaceAdded,
		UpdateFunc: e.namespaceUpdated,
	})
	e.nsLister = nsInformer.Lister()
	e.nsListerSynced = nsInformer.Informer().HasSynced

	e.syncHandler = e.syncNamespace

	return e, nil
}

// ServiceAccountsController manages ServiceAccount objects inside Namespaces
type ServiceAccountsController struct {
	client                  clientset.Interface
	serviceAccountsToEnsure []v1.ServiceAccount

	// To allow injection for testing.
	syncHandler func(key string) error

	saLister       corelisters.ServiceAccountLister
	saListerSynced cache.InformerSynced

	nsLister       corelisters.NamespaceLister
	nsListerSynced cache.InformerSynced

	queue workqueue.RateLimitingInterface
}

func (c *ServiceAccountsController) Run(workers int, stopCh <-chan struct{}) {
	defer utilruntime.HandleCrash()
	defer c.queue.ShutDown()

	glog.Infof("Starting service account controller")
	defer glog.Infof("Shutting down service account controller")

	if !controller.WaitForCacheSync("service account", stopCh, c.saListerSynced, c.nsListerSynced) {
		return
	}

	for i := 0; i < workers; i++ {
		go wait.Until(c.runWorker, time.Second, stopCh)
	}

	<-stopCh
}

// serviceAccountDeleted reacts to a ServiceAccount deletion by recreating a default ServiceAccount in the namespace if needed
func (c *ServiceAccountsController) serviceAccountDeleted(obj interface{}) {
	sa, ok := obj.(*v1.ServiceAccount)
	if !ok {
		tombstone, ok := obj.(cache.DeletedFinalStateUnknown)
		if !ok {
			utilruntime.HandleError(fmt.Errorf("Couldn't get object from tombstone %#v", obj))
			return
		}
		sa, ok = tombstone.Obj.(*v1.ServiceAccount)
		if !ok {
			utilruntime.HandleError(fmt.Errorf("Tombstone contained object that is not a ServiceAccount %#v", obj))
			return
		}
	}
	c.queue.Add(sa.Namespace)
}

// namespaceAdded reacts to a Namespace creation by creating a default ServiceAccount object
func (c *ServiceAccountsController) namespaceAdded(obj interface{}) {
	namespace := obj.(*v1.Namespace)
	c.queue.Add(namespace.Name)
}

// namespaceUpdated reacts to a Namespace update (or re-list) by creating a default ServiceAccount in the namespace if needed
func (c *ServiceAccountsController) namespaceUpdated(oldObj interface{}, newObj interface{}) {
	newNamespace := newObj.(*v1.Namespace)
	c.queue.Add(newNamespace.Name)
}

func (c *ServiceAccountsController) runWorker() {
	for c.processNextWorkItem() {
	}
}

// processNextWorkItem deals with one key off the queue.  It returns false when it's time to quit.
func (c *ServiceAccountsController) processNextWorkItem() bool {
	key, quit := c.queue.Get()
	if quit {
		return false
	}
	defer c.queue.Done(key)

	err := c.syncHandler(key.(string))
	if err == nil {
		c.queue.Forget(key)
		return true
	}

	utilruntime.HandleError(fmt.Errorf("%v failed with : %v", key, err))
	c.queue.AddRateLimited(key)

	return true
}
func (c *ServiceAccountsController) syncNamespace(key string) error {
	startTime := time.Now()
	defer func() {
		glog.V(4).Infof("Finished syncing namespace %q (%v)", key, time.Now().Sub(startTime))
	}()

	ns, err := c.nsLister.Get(key)
	if apierrs.IsNotFound(err) {
		return nil
	}
	if err != nil {
		return err
	}
	if ns.Status.Phase != v1.NamespaceActive {
		// If namespace is not active, we shouldn't try to create anything
		return nil
	}

	createFailures := []error{}
	for i := range c.serviceAccountsToEnsure {
		sa := c.serviceAccountsToEnsure[i]
		switch _, err := c.saLister.ServiceAccounts(ns.Name).Get(sa.Name); {
		case err == nil:
			continue
		case apierrs.IsNotFound(err):
		case err != nil:
			return err
		}
		// this is only safe because we never read it and we always write it
		// TODO eliminate this once the fake client can handle creation without NS
		sa.Namespace = ns.Name

		if _, err := c.client.CoreV1().ServiceAccounts(ns.Name).Create(&sa); err != nil && !apierrs.IsAlreadyExists(err) {
			createFailures = append(createFailures, err)
		}
	}

	return utilerrors.Flatten(utilerrors.NewAggregate(createFailures))
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/mitchellh/go-homedir"
	"github.com/rackspace/gophercloud"
	"github.com/rackspace/gophercloud/openstack"
	"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
	"github.com/rackspace/gophercloud/openstack/identity/v2/tokens"
	"github.com/rackspace/gophercloud/pagination"
)

const (
	CREDENTIAL_FILE      = ".novassh"
	CREDENTIAL_FILE_MODE = 0600
)

type machine struct {
	Name   string
	Ipaddr string
}

func newMechine(s servers.Server) (*machine, error) {
	m := &machine{
		Name: s.Name,
	}

	// For ConoHa
	for key, value := range s.Metadata {
		if key == "instance_name_tag" {
			m.Name = value.(string)
		}
	}

	// Detecting public IP address for connectiong SSH
	for name, addressSet := range s.Addresses {
		if name == "public" {
			// Response of rackspace API has the key either "public" or "private"

			// TODO: Rackspace API and other OpenStack Systems

		} else if strings.HasPrefix(name, "ext-") {
			// Response of ConoHa API has the prefix either "ext-" or "local-"
			as, ok := addressSet.([]interface{})
			if !ok {
				return nil, fmt.Errorf("Invalid address set(type assertion failed).")
			}

			for _, v := range as {
				addr, ok := v.(map[string]interface{})
				if !ok {
					return nil, fmt.Errorf("Invalid address set(type assertion failed).")
				}

				// TODO: support IPv6 address
				if addr["version"] == 4.0 {
					m.Ipaddr = addr["addr"].(string)
				}
			}
		}
	}

	return m, nil
}

type nova struct {
	servers  []servers.Server
	provider *gophercloud.ServiceClient
}

type Credential struct {
	ComputeEndpoint string
	*tokens.Token
}

func NewNova() *nova {
	nova := &nova{
		servers: nil,
	}
	return nova
}

func (n *nova) Init() (err error) {
	// Credentials from env
	opts, err := openstack.AuthOptionsFromEnv()
	if err != nil {
		return err
	}

	// Endpoint options
	eo := gophercloud.EndpointOpts{
		Type:   "compute",
		Region: os.Getenv("OS_REGION_NAME"),
	}

	// Try to use cached credential if file exists.
	_, err = os.Stat(n.credentialCachePath())
	if err == nil {
		// Use cache file
		strdata, err := ioutil.ReadFile(n.credentialCachePath())
		if err != nil {
			log.Warnf("Failed to load cache file: %v", err)
			goto AUTH
		}

		cred := &Credential{Token: &tokens.Token{}}
		if err = json.Unmarshal(strdata, cred); err != nil {
			log.Warnf("Failed to unmarchal the cache data: %v", err)
			goto AUTH
		}

		if time.Now().Before(cred.ExpiresAt) {
			log.Debugf("Token has expired. Try to reauth.")
			goto AUTH
		}

		client, err := openstack.NewClient(opts.IdentityEndpoint)
		client.EndpointLocator = func(o gophercloud.EndpointOpts) (string, error) {
			return cred.ComputeEndpoint, nil
		}
		client.TokenID = cred.ID

		// Set service client
		n.provider, err = openstack.NewComputeV2(client, eo)
		return nil
	}

AUTH:

	client, err := openstack.AuthenticatedClient(opts)
	if err != nil {
		return err
	}

	// Set service client
	n.provider, err = openstack.NewComputeV2(client, eo)

	// Store credential to cache file
	cred := &Credential{
		ComputeEndpoint: n.provider.Endpoint,
		Token: &tokens.Token{
			ID:        client.TokenID,
			ExpiresAt: time.Now(),
		},
	}
	strdata, err := json.Marshal(cred)
	if err != nil {
		return err
	}

	if err = ioutil.WriteFile(n.credentialCachePath(), strdata, CREDENTIAL_FILE_MODE); err != nil {
		log.Warnf("Can not write the credential cache file: %s", n.credentialCachePath())
	}

	return nil
}

func (n *nova) Find(name string) (m *machine, err error) {
	if n.servers == nil {
		n.servers, err = n.listServers()
		if err != nil {
			return nil, err
		}
	}

	// For Rackspace or other OpenStack systems
	for _, server := range n.servers {
		if server.Name == name {
			return newMechine(server)
		}
	}

	//  For ConoHa
	for _, server := range n.servers {
		instanceName, ok := server.Metadata["instance_name_tag"].(string)
		if ok && instanceName == name {
			return newMechine(server)
		}
	}
	return nil, nil
}

func (n *nova) listServers() ([]servers.Server, error) {
	pager := servers.List(n.provider, servers.ListOpts{})

	ss := []servers.Server{}
	pager.EachPage(func(page pagination.Page) (bool, error) {
		sss, err := servers.ExtractServers(page)
		if err != nil {
			return false, err
		}
		ss = append(ss, sss...)
		return true, nil
	})

	return ss, nil
}

func (n *nova) credentialCachePath() string {
	d, err := homedir.Dir()
	if err == nil {
		return fmt.Sprintf("%s%c%s", d, filepath.Separator, CREDENTIAL_FILE)
	} else {
		return CREDENTIAL_FILE
	}
}
<commit_msg>typo修正<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/mitchellh/go-homedir"
	"github.com/rackspace/gophercloud"
	"github.com/rackspace/gophercloud/openstack"
	"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
	"github.com/rackspace/gophercloud/openstack/identity/v2/tokens"
	"github.com/rackspace/gophercloud/pagination"
)

const (
	CREDENTIAL_FILE      = ".novassh"
	CREDENTIAL_FILE_MODE = 0600
)

type machine struct {
	Name   string
	Ipaddr string
}

func newMachine(s servers.Server) (*machine, error) {
	m := &machine{
		Name: s.Name,
	}

	// For ConoHa
	for key, value := range s.Metadata {
		if key == "instance_name_tag" {
			m.Name = value.(string)
		}
	}

	// Detecting public IP address for connectiong SSH
	for name, addressSet := range s.Addresses {
		if name == "public" {
			// Response of rackspace API has the key either "public" or "private"

			// TODO: Rackspace API and other OpenStack Systems

		} else if strings.HasPrefix(name, "ext-") {
			// Response of ConoHa API has the prefix either "ext-" or "local-"
			as, ok := addressSet.([]interface{})
			if !ok {
				return nil, fmt.Errorf("Invalid address set(type assertion failed).")
			}

			for _, v := range as {
				addr, ok := v.(map[string]interface{})
				if !ok {
					return nil, fmt.Errorf("Invalid address set(type assertion failed).")
				}

				// TODO: support IPv6 address
				if addr["version"] == 4.0 {
					m.Ipaddr = addr["addr"].(string)
				}
			}
		}
	}

	return m, nil
}

type nova struct {
	servers  []servers.Server
	provider *gophercloud.ServiceClient
}

type Credential struct {
	ComputeEndpoint string
	*tokens.Token
}

func NewNova() *nova {
	nova := &nova{
		servers: nil,
	}
	return nova
}

func (n *nova) Init() (err error) {
	// Credentials from env
	opts, err := openstack.AuthOptionsFromEnv()
	if err != nil {
		return err
	}

	// Endpoint options
	eo := gophercloud.EndpointOpts{
		Type:   "compute",
		Region: os.Getenv("OS_REGION_NAME"),
	}

	// Try to use cached credential if file exists.
	_, err = os.Stat(n.credentialCachePath())
	if err == nil {
		// Use cache file
		strdata, err := ioutil.ReadFile(n.credentialCachePath())
		if err != nil {
			log.Warnf("Failed to load cache file: %v", err)
			goto AUTH
		}

		cred := &Credential{Token: &tokens.Token{}}
		if err = json.Unmarshal(strdata, cred); err != nil {
			log.Warnf("Failed to unmarchal the cache data: %v", err)
			goto AUTH
		}

		if time.Now().Before(cred.ExpiresAt) {
			log.Debugf("Token has expired. Try to reauth.")
			goto AUTH
		}

		client, err := openstack.NewClient(opts.IdentityEndpoint)
		client.EndpointLocator = func(o gophercloud.EndpointOpts) (string, error) {
			return cred.ComputeEndpoint, nil
		}
		client.TokenID = cred.ID

		// Set service client
		n.provider, err = openstack.NewComputeV2(client, eo)
		return nil
	}

AUTH:

	client, err := openstack.AuthenticatedClient(opts)
	if err != nil {
		return err
	}

	// Set service client
	n.provider, err = openstack.NewComputeV2(client, eo)

	// Store credential to cache file
	cred := &Credential{
		ComputeEndpoint: n.provider.Endpoint,
		Token: &tokens.Token{
			ID:        client.TokenID,
			ExpiresAt: time.Now(),
		},
	}
	strdata, err := json.Marshal(cred)
	if err != nil {
		return err
	}

	if err = ioutil.WriteFile(n.credentialCachePath(), strdata, CREDENTIAL_FILE_MODE); err != nil {
		log.Warnf("Can not write the credential cache file: %s", n.credentialCachePath())
	}

	return nil
}

func (n *nova) Find(name string) (m *machine, err error) {
	if n.servers == nil {
		n.servers, err = n.listServers()
		if err != nil {
			return nil, err
		}
	}

	// For Rackspace or other OpenStack systems
	for _, server := range n.servers {
		if server.Name == name {
			return newMachine(server)
		}
	}

	//  For ConoHa
	for _, server := range n.servers {
		instanceName, ok := server.Metadata["instance_name_tag"].(string)
		if ok && instanceName == name {
			return newMachine(server)
		}
	}
	return nil, nil
}

func (n *nova) listServers() ([]servers.Server, error) {
	pager := servers.List(n.provider, servers.ListOpts{})

	ss := []servers.Server{}
	pager.EachPage(func(page pagination.Page) (bool, error) {
		sss, err := servers.ExtractServers(page)
		if err != nil {
			return false, err
		}
		ss = append(ss, sss...)
		return true, nil
	})

	return ss, nil
}

func (n *nova) credentialCachePath() string {
	d, err := homedir.Dir()
	if err == nil {
		return fmt.Sprintf("%s%c%s", d, filepath.Separator, CREDENTIAL_FILE)
	} else {
		return CREDENTIAL_FILE
	}
}
<|endoftext|>


<commit_before>package opts

import (
	"fmt"
	"net"
	"net/url"
	"os"
	"path/filepath"
	"regexp"
	"strings"

	"github.com/docker/docker/api"
	flag "github.com/docker/docker/pkg/mflag"
	"github.com/docker/docker/pkg/parsers"
)

func ListVar(values *[]string, names []string, usage string) {
	flag.Var(newListOptsRef(values, nil), names, usage)
}

func HostListVar(values *[]string, names []string, usage string) {
	flag.Var(newListOptsRef(values, api.ValidateHost), names, usage)
}

func IPListVar(values *[]string, names []string, usage string) {
	flag.Var(newListOptsRef(values, ValidateIPAddress), names, usage)
}

func DnsSearchListVar(values *[]string, names []string, usage string) {
	flag.Var(newListOptsRef(values, ValidateDnsSearch), names, usage)
}

func IPVar(value *net.IP, names []string, defaultValue, usage string) {
	flag.Var(NewIpOpt(value, defaultValue), names, usage)
}

func MirrorListVar(values *[]string, names []string, usage string) {
	flag.Var(newListOptsRef(values, ValidateMirror), names, usage)
}

// ListOpts type
type ListOpts struct {
	values    *[]string
	validator ValidatorFctType
}

func NewListOpts(validator ValidatorFctType) ListOpts {
	var values []string
	return *newListOptsRef(&values, validator)
}

func newListOptsRef(values *[]string, validator ValidatorFctType) *ListOpts {
	return &ListOpts{
		values:    values,
		validator: validator,
	}
}

func (opts *ListOpts) String() string {
	return fmt.Sprintf("%v", []string((*opts.values)))
}

// Set validates if needed the input value and add it to the
// internal slice.
func (opts *ListOpts) Set(value string) error {
	if opts.validator != nil {
		v, err := opts.validator(value)
		if err != nil {
			return err
		}
		value = v
	}
	(*opts.values) = append((*opts.values), value)
	return nil
}

// Delete remove the given element from the slice.
func (opts *ListOpts) Delete(key string) {
	for i, k := range *opts.values {
		if k == key {
			(*opts.values) = append((*opts.values)[:i], (*opts.values)[i+1:]...)
			return
		}
	}
}

// GetMap returns the content of values in a map in order to avoid
// duplicates.
// FIXME: can we remove this?
func (opts *ListOpts) GetMap() map[string]struct{} {
	ret := make(map[string]struct{})
	for _, k := range *opts.values {
		ret[k] = struct{}{}
	}
	return ret
}

// GetAll returns the values' slice.
// FIXME: Can we remove this?
func (opts *ListOpts) GetAll() []string {
	return (*opts.values)
}

// Get checks the existence of the given key.
func (opts *ListOpts) Get(key string) bool {
	for _, k := range *opts.values {
		if k == key {
			return true
		}
	}
	return false
}

// Len returns the amount of element in the slice.
func (opts *ListOpts) Len() int {
	return len((*opts.values))
}

// Validators
type ValidatorFctType func(val string) (string, error)

func ValidateAttach(val string) (string, error) {
	s := strings.ToLower(val)
	for _, str := range []string{"stdin", "stdout", "stderr"} {
		if s == str {
			return s, nil
		}
	}
	return val, fmt.Errorf("valid streams are STDIN, STDOUT and STDERR.")
}

func ValidateLink(val string) (string, error) {
	if _, err := parsers.PartParser("name:alias", val); err != nil {
		return val, err
	}
	return val, nil
}

func ValidatePath(val string) (string, error) {
	var containerPath string

	if strings.Count(val, ":") > 2 {
		return val, fmt.Errorf("bad format for volumes: %s", val)
	}

	splited := strings.SplitN(val, ":", 2)
	if len(splited) == 1 {
		containerPath = splited[0]
		val = filepath.Clean(splited[0])
	} else {
		containerPath = splited[1]
		val = fmt.Sprintf("%s:%s", splited[0], filepath.Clean(splited[1]))
	}

	if !filepath.IsAbs(containerPath) {
		return val, fmt.Errorf("%s is not an absolute path", containerPath)
	}
	return val, nil
}

func ValidateEnv(val string) (string, error) {
	arr := strings.Split(val, "=")
	if len(arr) > 1 {
		return val, nil
	}
	return fmt.Sprintf("%s=%s", val, os.Getenv(val)), nil
}

func ValidateIPAddress(val string) (string, error) {
	var ip = net.ParseIP(strings.TrimSpace(val))
	if ip != nil {
		return ip.String(), nil
	}
	return "", fmt.Errorf("%s is not an ip address", val)
}

// Validates domain for resolvconf search configuration.
// A zero length domain is represented by .
func ValidateDnsSearch(val string) (string, error) {
	if val = strings.Trim(val, " "); val == "." {
		return val, nil
	}
	return validateDomain(val)
}

func validateDomain(val string) (string, error) {
	alpha := regexp.MustCompile(`[a-zA-Z]`)
	if alpha.FindString(val) == "" {
		return "", fmt.Errorf("%s is not a valid domain", val)
	}
	re := regexp.MustCompile(`^(:?(:?[a-zA-Z0-9]|(:?[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9]))(:?\.(:?[a-zA-Z0-9]|(:?[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])))*)\.?\s*$`)
	ns := re.FindSubmatch([]byte(val))
	if len(ns) > 0 {
		return string(ns[1]), nil
	}
	return "", fmt.Errorf("%s is not a valid domain", val)
}

// Validates an HTTP(S) registry mirror
func ValidateMirror(val string) (string, error) {
	uri, err := url.Parse(val)
	if err != nil {
		return "", fmt.Errorf("%s is not a valid URI", val)
	}

	if uri.Scheme != "http" && uri.Scheme != "https" {
		return "", fmt.Errorf("Unsupported scheme %s", uri.Scheme)
	}

	if uri.Path != "" || uri.RawQuery != "" || uri.Fragment != "" {
		return "", fmt.Errorf("Unsupported path/query/fragment at end of the URI")
	}

	return fmt.Sprintf("%s://%s/v1/", uri.Scheme, uri.Host), nil
}
<commit_msg>Refactor all pre-compiled regexp to package level vars Addresses #8057<commit_after>package opts

import (
	"fmt"
	"net"
	"net/url"
	"os"
	"path/filepath"
	"regexp"
	"strings"

	"github.com/docker/docker/api"
	flag "github.com/docker/docker/pkg/mflag"
	"github.com/docker/docker/pkg/parsers"
)

var (
	alphaRegexp  = regexp.MustCompile(`[a-zA-Z]`)
	domainRegexp = regexp.MustCompile(`^(:?(:?[a-zA-Z0-9]|(:?[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9]))(:?\.(:?[a-zA-Z0-9]|(:?[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])))*)\.?\s*$`)
)

func ListVar(values *[]string, names []string, usage string) {
	flag.Var(newListOptsRef(values, nil), names, usage)
}

func HostListVar(values *[]string, names []string, usage string) {
	flag.Var(newListOptsRef(values, api.ValidateHost), names, usage)
}

func IPListVar(values *[]string, names []string, usage string) {
	flag.Var(newListOptsRef(values, ValidateIPAddress), names, usage)
}

func DnsSearchListVar(values *[]string, names []string, usage string) {
	flag.Var(newListOptsRef(values, ValidateDnsSearch), names, usage)
}

func IPVar(value *net.IP, names []string, defaultValue, usage string) {
	flag.Var(NewIpOpt(value, defaultValue), names, usage)
}

func MirrorListVar(values *[]string, names []string, usage string) {
	flag.Var(newListOptsRef(values, ValidateMirror), names, usage)
}

// ListOpts type
type ListOpts struct {
	values    *[]string
	validator ValidatorFctType
}

func NewListOpts(validator ValidatorFctType) ListOpts {
	var values []string
	return *newListOptsRef(&values, validator)
}

func newListOptsRef(values *[]string, validator ValidatorFctType) *ListOpts {
	return &ListOpts{
		values:    values,
		validator: validator,
	}
}

func (opts *ListOpts) String() string {
	return fmt.Sprintf("%v", []string((*opts.values)))
}

// Set validates if needed the input value and add it to the
// internal slice.
func (opts *ListOpts) Set(value string) error {
	if opts.validator != nil {
		v, err := opts.validator(value)
		if err != nil {
			return err
		}
		value = v
	}
	(*opts.values) = append((*opts.values), value)
	return nil
}

// Delete remove the given element from the slice.
func (opts *ListOpts) Delete(key string) {
	for i, k := range *opts.values {
		if k == key {
			(*opts.values) = append((*opts.values)[:i], (*opts.values)[i+1:]...)
			return
		}
	}
}

// GetMap returns the content of values in a map in order to avoid
// duplicates.
// FIXME: can we remove this?
func (opts *ListOpts) GetMap() map[string]struct{} {
	ret := make(map[string]struct{})
	for _, k := range *opts.values {
		ret[k] = struct{}{}
	}
	return ret
}

// GetAll returns the values' slice.
// FIXME: Can we remove this?
func (opts *ListOpts) GetAll() []string {
	return (*opts.values)
}

// Get checks the existence of the given key.
func (opts *ListOpts) Get(key string) bool {
	for _, k := range *opts.values {
		if k == key {
			return true
		}
	}
	return false
}

// Len returns the amount of element in the slice.
func (opts *ListOpts) Len() int {
	return len((*opts.values))
}

// Validators
type ValidatorFctType func(val string) (string, error)

func ValidateAttach(val string) (string, error) {
	s := strings.ToLower(val)
	for _, str := range []string{"stdin", "stdout", "stderr"} {
		if s == str {
			return s, nil
		}
	}
	return val, fmt.Errorf("valid streams are STDIN, STDOUT and STDERR.")
}

func ValidateLink(val string) (string, error) {
	if _, err := parsers.PartParser("name:alias", val); err != nil {
		return val, err
	}
	return val, nil
}

func ValidatePath(val string) (string, error) {
	var containerPath string

	if strings.Count(val, ":") > 2 {
		return val, fmt.Errorf("bad format for volumes: %s", val)
	}

	splited := strings.SplitN(val, ":", 2)
	if len(splited) == 1 {
		containerPath = splited[0]
		val = filepath.Clean(splited[0])
	} else {
		containerPath = splited[1]
		val = fmt.Sprintf("%s:%s", splited[0], filepath.Clean(splited[1]))
	}

	if !filepath.IsAbs(containerPath) {
		return val, fmt.Errorf("%s is not an absolute path", containerPath)
	}
	return val, nil
}

func ValidateEnv(val string) (string, error) {
	arr := strings.Split(val, "=")
	if len(arr) > 1 {
		return val, nil
	}
	return fmt.Sprintf("%s=%s", val, os.Getenv(val)), nil
}

func ValidateIPAddress(val string) (string, error) {
	var ip = net.ParseIP(strings.TrimSpace(val))
	if ip != nil {
		return ip.String(), nil
	}
	return "", fmt.Errorf("%s is not an ip address", val)
}

// Validates domain for resolvconf search configuration.
// A zero length domain is represented by .
func ValidateDnsSearch(val string) (string, error) {
	if val = strings.Trim(val, " "); val == "." {
		return val, nil
	}
	return validateDomain(val)
}

func validateDomain(val string) (string, error) {
	if alphaRegexp.FindString(val) == "" {
		return "", fmt.Errorf("%s is not a valid domain", val)
	}
	ns := domainRegexp.FindSubmatch([]byte(val))
	if len(ns) > 0 {
		return string(ns[1]), nil
	}
	return "", fmt.Errorf("%s is not a valid domain", val)
}

// Validates an HTTP(S) registry mirror
func ValidateMirror(val string) (string, error) {
	uri, err := url.Parse(val)
	if err != nil {
		return "", fmt.Errorf("%s is not a valid URI", val)
	}

	if uri.Scheme != "http" && uri.Scheme != "https" {
		return "", fmt.Errorf("Unsupported scheme %s", uri.Scheme)
	}

	if uri.Path != "" || uri.RawQuery != "" || uri.Fragment != "" {
		return "", fmt.Errorf("Unsupported path/query/fragment at end of the URI")
	}

	return fmt.Sprintf("%s://%s/v1/", uri.Scheme, uri.Host), nil
}
<|endoftext|>


<commit_before>// Copyright 2014 The sutil Author. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package snetutil

import (
	"fmt"
	"bytes"
	"strconv"
	"io/ioutil"
	"net/http"
	"net/url"
	"encoding/json"
    "github.com/julienschmidt/httprouter"

	"github.com/shawnfeng/sutil/slog"
)

// http response interface
type HttpResponse interface {
	Marshal() (int, []byte)
}

// 定义了几种产用的类型的response

// json形式的response
type HttpRespJson struct {
	status int
	resp interface{}
}

func (m *HttpRespJson) Marshal() (int, []byte) {
	fun := "HttpRespJson.Marshal -->"
	resp, err := json.Marshal(m.resp)

	if err != nil {
		slog.Warnf("%s json unmarshal err:%s", fun, err)
	}

	return m.status, resp
}

func NewHttpRespJson200(r interface{}) HttpResponse {
	return &HttpRespJson{200, r}
}

func NewHttpRespJson(status int, r interface{}) HttpResponse {
	return &HttpRespJson{status, r}
}

// byte 形式的response
type HttpRespBytes struct {
	status int
	resp []byte
}

func (m *HttpRespBytes) Marshal() (int, []byte) {
	return m.status, m.resp
}

func NewHttpRespBytes(status int, resp []byte) HttpResponse {
	return &HttpRespBytes{status, resp}
}

// string 形式的response
type HttpRespString struct {
	status int
	resp string
}

func (m *HttpRespString) Marshal() (int, []byte) {
	return m.status, []byte(m.resp)
}

func NewHttpRespString(status int, resp string) HttpResponse {
	return &HttpRespString{status, resp}
}

// ===============================================
type keyGet interface {
	Get(key string) string
}

type reqArgs struct {
	r keyGet
}

func NewreqArgs(r keyGet) *reqArgs {
	return &reqArgs{r}
}

func (m *reqArgs) String(key string) string {
	return m.r.Get(key)
}

func (m *reqArgs) Int(key string) int {
	fun := "reqArgs.Int -->"
	v := m.r.Get(key)
	if len(v) == 0 {
		return 0
	}

	i, err := strconv.Atoi(v)
	if err != nil {
		slog.Warnf("%s parse int v:%s err:%s", fun, v, err)
	}
	return i

}

func (m *reqArgs) Int32(key string) int32 {
	fun := "reqArgs.Int32 -->"
	v := m.r.Get(key)
	if len(v) == 0 {
		return 0
	}

	i, err := strconv.ParseInt(v, 10, 32)
	if err != nil {
		slog.Warnf("%s parse int32 v:%s err:%s", fun, v, err)
	}

	return int32(i)
}

func (m *reqArgs) Int64(key string) int64 {
	fun := "reqArgs.Int64 -->"
	v := m.r.Get(key)
	if len(v) == 0 {
		return 0
	}

	i, err := strconv.ParseInt(v, 10, 64)
	if err != nil {
		slog.Warnf("%s parse int64 v:%s err:%s", fun, v, err)
	}
	return i

}

func (m *reqArgs) Bool(key string) bool {
	fun := "reqArgs.Bool -->"
	v := m.r.Get(key)
	if len(v) == 0 {
		return false
	}

	i, err := strconv.ParseBool(v)
	if err != nil {
		slog.Warnf("%s parse int64 v:%s err:%s", fun, v, err)
	}
	return i

}

// =========================
type reqQuery struct {
	r *http.Request
	q url.Values
}

func (m *reqQuery) Get(key string) string {
	fun := "reqQuery.Get -->"
	if m.q == nil {
		if m.r.URL != nil {
			var err error
			m.q, err = url.ParseQuery(m.r.URL.RawQuery)
			if err != nil {
				slog.Warnf("%s parse query q:%s err:%s", fun, m.r.URL.RawQuery, err)
			}
		}

		if m.q == nil {
			m.q = make(url.Values)
		}

		slog.Debugf("%s parse query q:%s err:%s", fun, m.r.URL.RawQuery, m.q)
	}

	return m.q.Get(key)
}

type reqParams struct {
	p httprouter.Params
}

func (m *reqParams) Get(key string) string {
	return m.p.ByName(key)
}

// ==========
type reqBody struct {
	body []byte
}

func (m *reqBody) Binary() []byte {
	return m.body
}

func (m *reqBody) Json(js interface{}) error {

    dc := json.NewDecoder(bytes.NewBuffer(m.body))
    dc.UseNumber()
    err := dc.Decode(js)
	if err != nil {
		return fmt.Errorf("json unmarshal %s", err.Error())
	} else {
		return nil
	}

}

func NewreqBody(body []byte) *reqBody {
	return &reqBody {
		body: body,
	}

}

// ============
type reqMultiBody struct {

}

// ============================
// 没有body类的请求
type HttpRequestNoBody struct {
	r *http.Request

	query *reqArgs
	params *reqArgs

}

func (m *HttpRequestNoBody) Query() *reqArgs {
	return m.query
}

func (m *HttpRequestNoBody) Params() *reqArgs {
	return m.params
}

func (m *HttpRequestNoBody) URL() *url.URL {
	return m.r.URL
}

func (m *HttpRequestNoBody) Method() string {
	return m.r.Method
}

func (m *HttpRequestNoBody) RemoteAddr() string {
	return m.r.RemoteAddr
}

func (m *HttpRequestNoBody) Header() http.Header {
	return m.r.Header
}

func (m *HttpRequestNoBody) Request() *http.Request {
	return m.r
}

func NewHttpRequestNoBody(r *http.Request, ps httprouter.Params) (*HttpRequestNoBody, error) {
	return &HttpRequestNoBody {
		r: r,
		query: NewreqArgs(&reqQuery{r: r,}),
		params: NewreqArgs(&reqParams{ps}),
	}, nil
}

// ============================
type HttpRequestCommonBody struct {
	HttpRequestNoBody
	body *reqBody
}

func (m *HttpRequestCommonBody) Body() *reqBody {
	return m.body
}

func NewHttpRequestCommonBody(r *http.Request, ps httprouter.Params) (*HttpRequestCommonBody, error) {

	body, err := ioutil.ReadAll(r.Body);
	if err != nil {
		return nil, fmt.Errorf("read body %s", err.Error())
	}

	return &HttpRequestCommonBody {
		HttpRequestNoBody: HttpRequestNoBody{r, NewreqArgs(&reqQuery{r: r}), NewreqArgs(&reqParams{ps})},
		body: NewreqBody(body),
	}, nil
}

func NewHttpRequestJsonBody(r *http.Request, ps httprouter.Params, js interface{}) (*HttpRequestCommonBody, error) {
	hrb, err := NewHttpRequestCommonBody(r, ps)
	if err != nil {
		return hrb, err
	}

	err = hrb.Body().Json(js)
	if err != nil {
		return nil, fmt.Errorf("json unmarshal %s", err.Error())
	}

	return hrb, nil

}

type HandleNoBody interface {
	Handle(*HttpRequestNoBody) HttpResponse
	// 构造自己一个副本，如果结构本身保存了
	// 本请求的数据，则factory必须new一个新的
	// 否则没有共享数据问题，可以返回自己当前的指针就好
	Factory() HandleNoBody
}

type HandleCommonBody interface {
	Handle(*HttpRequestCommonBody) HttpResponse

	// 对于存在body，并使用json自动unmarshal
	// 一定注意使用一般时候你都需要new一个新的
	// 除非你想让请求之间通过某种技巧来关联，否则。。。
	Factory() HandleCommonBody
}

func HttpNoBodyWrapper(h HandleNoBody) func(http.ResponseWriter, *http.Request, httprouter.Params) {
	fun := "HttpNoBodyWrapper -->"

	return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
		req, err := NewHttpRequestNoBody(r, ps)
		if err != nil {
			slog.Warnf("%s body json err:%s", fun, err)
			http.Error(w, "request err", 400)
			return
		}

		resp := h.Factory().Handle(req)
		status, rs := resp.Marshal()

		if status == 200 {
			fmt.Fprintf(w, "%s", rs)
		} else {
			http.Error(w, string(rs), status)
		}
	}

}

func HttpCommonBodyWrapper(h HandleCommonBody) func(http.ResponseWriter, *http.Request, httprouter.Params) {
	fun := "HttpCommmonBodyWrapper -->"

	return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
		req, err := NewHttpRequestCommonBody(r, ps)
		if err != nil {
			slog.Warnf("%s req body err:%s", fun, err)
			http.Error(w, "request err", 400)
			return
		}

		resp := h.Factory().Handle(req)
		status, rs := resp.Marshal()

		if status == 200 {
			fmt.Fprintf(w, "%s", rs)
		} else {
			http.Error(w, string(rs), status)
		}
	}

}

func HttpJsonBodyWrapper(h HandleCommonBody) func(http.ResponseWriter, *http.Request, httprouter.Params) {
	fun := "HttpJsonBodyWrapper -->"

	return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {

		newme := h.Factory()
		req, err := NewHttpRequestJsonBody(r, ps, newme)
		if err != nil {
			slog.Warnf("%s body json err:%s", fun, err)
			http.Error(w, "request err", 400)
			return
		}

		resp := newme.Handle(req)
		status, rs := resp.Marshal()

		if status == 200 {
			fmt.Fprintf(w, "%s", rs)
		} else {
			http.Error(w, string(rs), status)
		}
	}

}
<commit_msg>http req<commit_after>// Copyright 2014 The sutil Author. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package snetutil

import (
	"fmt"
	"bytes"
	"strings"
	"strconv"
	"io/ioutil"
	"net/http"
	"mime"
	"mime/multipart"
	"net/url"
	"encoding/json"
    "github.com/julienschmidt/httprouter"

	"github.com/shawnfeng/sutil/slog"
)

// http response interface
type HttpResponse interface {
	Marshal() (int, []byte)
}

// 定义了几种产用的类型的response

// json形式的response
type HttpRespJson struct {
	status int
	resp interface{}
}

func (m *HttpRespJson) Marshal() (int, []byte) {
	fun := "HttpRespJson.Marshal -->"
	resp, err := json.Marshal(m.resp)

	if err != nil {
		slog.Warnf("%s json unmarshal err:%s", fun, err)
	}

	return m.status, resp
}

func NewHttpRespJson200(r interface{}) HttpResponse {
	return &HttpRespJson{200, r}
}

func NewHttpRespJson(status int, r interface{}) HttpResponse {
	return &HttpRespJson{status, r}
}

// byte 形式的response
type HttpRespBytes struct {
	status int
	resp []byte
}

func (m *HttpRespBytes) Marshal() (int, []byte) {
	return m.status, m.resp
}

func NewHttpRespBytes(status int, resp []byte) HttpResponse {
	return &HttpRespBytes{status, resp}
}

// string 形式的response
type HttpRespString struct {
	status int
	resp string
}

func (m *HttpRespString) Marshal() (int, []byte) {
	return m.status, []byte(m.resp)
}

func NewHttpRespString(status int, resp string) HttpResponse {
	return &HttpRespString{status, resp}
}

// ===============================================
type keyGet interface {
	Get(key string) string
}

type reqArgs struct {
	r keyGet
}

func NewreqArgs(r keyGet) *reqArgs {
	return &reqArgs{r}
}

func (m *reqArgs) String(key string) string {
	return m.r.Get(key)
}

func (m *reqArgs) Int(key string) int {
	fun := "reqArgs.Int -->"
	v := m.r.Get(key)
	if len(v) == 0 {
		return 0
	}

	i, err := strconv.Atoi(v)
	if err != nil {
		slog.Warnf("%s parse int v:%s err:%s", fun, v, err)
	}
	return i

}

func (m *reqArgs) Int32(key string) int32 {
	fun := "reqArgs.Int32 -->"
	v := m.r.Get(key)
	if len(v) == 0 {
		return 0
	}

	i, err := strconv.ParseInt(v, 10, 32)
	if err != nil {
		slog.Warnf("%s parse int32 v:%s err:%s", fun, v, err)
	}

	return int32(i)
}

func (m *reqArgs) Int64(key string) int64 {
	fun := "reqArgs.Int64 -->"
	v := m.r.Get(key)
	if len(v) == 0 {
		return 0
	}

	i, err := strconv.ParseInt(v, 10, 64)
	if err != nil {
		slog.Warnf("%s parse int64 v:%s err:%s", fun, v, err)
	}
	return i

}

func (m *reqArgs) Bool(key string) bool {
	fun := "reqArgs.Bool -->"
	v := m.r.Get(key)
	if len(v) == 0 {
		return false
	}

	i, err := strconv.ParseBool(v)
	if err != nil {
		slog.Warnf("%s parse int64 v:%s err:%s", fun, v, err)
	}
	return i

}

// =========================
type reqQuery struct {
	r *http.Request
	q url.Values
}

func (m *reqQuery) Get(key string) string {
	fun := "reqQuery.Get -->"
	if m.q == nil {
		if m.r.URL != nil {
			var err error
			m.q, err = url.ParseQuery(m.r.URL.RawQuery)
			if err != nil {
				slog.Warnf("%s parse query q:%s err:%s", fun, m.r.URL.RawQuery, err)
			}
		}

		if m.q == nil {
			m.q = make(url.Values)
		}

		slog.Debugf("%s parse query q:%s err:%s", fun, m.r.URL.RawQuery, m.q)
	}

	return m.q.Get(key)
}

type reqParams struct {
	p httprouter.Params
}

func (m *reqParams) Get(key string) string {
	return m.p.ByName(key)
}

// ==========
type reqBody struct {
	r *http.Request
	body []byte
}

func (m *reqBody) Binary() []byte {
	fun := "reqBody.Binary"
	if m.body == nil {
		body, err := ioutil.ReadAll(m.r.Body);
		if err != nil {
			slog.Errorf("%s read body %s", fun, err.Error())
		}
		m.body = body
	}

	return m.body
}

func (m *reqBody) Json(js interface{}) error {

    dc := json.NewDecoder(bytes.NewBuffer(m.Binary()))
    dc.UseNumber()
    err := dc.Decode(js)
	if err != nil {
		return fmt.Errorf("json unmarshal %s", err.Error())
	} else {
		return nil
	}

}

func (m *reqBody) FormValue(key string) string {
	fun := "reqBody.FormValue -->"
	// 获取到content-type，并根据其类型来决策是从r.MultipartForm，获取数据
	// 还是r.PostForm中获取数据，r.Form实际上市把query中的postform中的，mutlpartform都搞到一起了
	// r.PostFrom 对应的content-type为 application/x-www-form-urlencoded
	// r.MultipartForm 对应的 multipart/form-data

	// 仅仅是为让内部触发对form的parse过程
	m.r.FormValue(key)

	// 参照http package中parsePostForm 实现
	ct := m.r.Header.Get("Content-Type")
	// RFC 2616, section 7.2.1 - empty type
	//   SHOULD be treated as application/octet-stream
	if ct == "" {
		ct = "application/octet-stream"
	}
	var err error
	ct, _, err = mime.ParseMediaType(ct)
	if err != nil {
		slog.Errorf("%s parsemediatype err:%s", fun, err)
	}

	if ct == "application/x-www-form-urlencoded" {
		if vs := m.r.PostForm[key]; len(vs) > 0 {
			return vs[0]
		}

	} else if ct == "multipart/form-data" {
		if vs := m.r.MultipartForm.Value[key]; len(vs) > 0 {
			return vs[0]
		}

	}

	return ""
}

func (m *reqBody) FormValueJson(key string, js interface{}) error {

    dc := json.NewDecoder(strings.NewReader(m.FormValue(key)))
    dc.UseNumber()
    err := dc.Decode(js)
	if err != nil {
		return fmt.Errorf("json unmarshal %s", err.Error())
	} else {
		return nil
	}
}

func (m *reqBody) FormFile(key string) ([]byte, *multipart.FileHeader, error) {

	file, head, err := m.r.FormFile(key)
	if err != nil {
		return nil, nil, fmt.Errorf("get form file err:%s", err)
	}

    data, err := ioutil.ReadAll(file)
	if err != nil {
		return nil, nil, fmt.Errorf("get form file data err:%s", err)
	}

	return data, head, nil

}

// ============================
// 没有body类的请求
type HttpRequest struct {
	r *http.Request

	query *reqArgs
	params *reqArgs

	body *reqBody
}

func (m *HttpRequest) Query() *reqArgs {
	return m.query
}

func (m *HttpRequest) Params() *reqArgs {
	return m.params
}

func (m *HttpRequest) Body() *reqBody {
	return m.body
}

func (m *HttpRequest) URL() *url.URL {
	return m.r.URL
}

func (m *HttpRequest) Method() string {
	return m.r.Method
}

func (m *HttpRequest) RemoteAddr() string {
	return m.r.RemoteAddr
}

func (m *HttpRequest) Header() http.Header {
	return m.r.Header
}

func (m *HttpRequest) Request() *http.Request {
	return m.r
}

func NewHttpRequest(r *http.Request, ps httprouter.Params) (*HttpRequest, error) {
	return &HttpRequest {
		r: r,
		query: NewreqArgs(&reqQuery{r: r,}),
		params: NewreqArgs(&reqParams{ps}),
		body: &reqBody{r: r,},
	}, nil
}

func NewHttpRequestJsonBody(r *http.Request, ps httprouter.Params, js interface{}) (*HttpRequest, error) {
	hrb, err := NewHttpRequest(r, ps)
	if err != nil {
		return hrb, err
	}

	err = hrb.Body().Json(js)
	if err != nil {
		return nil, fmt.Errorf("json unmarshal %s", err.Error())
	}

	return hrb, nil

}

type HandleRequest interface {
	Handle(*HttpRequest) HttpResponse
	// 构造自己一个副本，如果结构本身保存了
	// 本请求的数据，则factory必须new一个新的
	// 否则没有共享数据问题，可以返回自己当前的指针就好

	// 对于存在body，并使用json自动unmarshal
	// 一定注意使用一般时候你都需要new一个新的
	// 除非你想让请求之间通过某种技巧来关联，否则。。。
	Factory() HandleRequest
}

func HttpRequestWrapper(h HandleRequest) func(http.ResponseWriter, *http.Request, httprouter.Params) {
	fun := "HttpRequestWrapper -->"

	return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
		req, err := NewHttpRequest(r, ps)
		if err != nil {
			slog.Warnf("%s body json err:%s", fun, err)
			http.Error(w, "request err", 400)
			return
		}

		resp := h.Factory().Handle(req)
		status, rs := resp.Marshal()

		if status == 200 {
			fmt.Fprintf(w, "%s", rs)
		} else {
			http.Error(w, string(rs), status)
		}
	}

}

func HttpRequestJsonBodyWrapper(h HandleRequest) func(http.ResponseWriter, *http.Request, httprouter.Params) {
	fun := "HttpRequestJsonBodyWrapper -->"

	return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {

		newme := h.Factory()
		req, err := NewHttpRequestJsonBody(r, ps, newme)
		if err != nil {
			slog.Warnf("%s body json err:%s", fun, err)
			http.Error(w, "request err", 400)
			return
		}

		resp := newme.Handle(req)
		status, rs := resp.Marshal()

		if status == 200 {
			fmt.Fprintf(w, "%s", rs)
		} else {
			http.Error(w, string(rs), status)
		}
	}

}

// 测试get 获取body ok
// 测试mutlibody 直接获取body,ok
// 测试 application/x-www-form-urlencoded
// 测试 multipart/form-data
<|endoftext|>


<commit_before>package fuse

import (
	"fmt"
	"log"
	"json"
	"os"
	"syscall"
	"testing"
)

type MutableDataFile struct {
	data []byte
	os.FileInfo
	GetAttrCalled bool
}

func (me *MutableDataFile) Read(r *ReadIn, bp *BufferPool) ([]byte, Status) {
	return me.data[r.Offset : r.Offset+uint64(r.Size)], OK
}

func (me *MutableDataFile) Write(w *WriteIn, d []byte) (uint32, Status) {
	end := uint64(w.Size) + w.Offset
	if int(end) > len(me.data) {
		data := make([]byte, len(me.data), end)
		copy(data, me.data)
		me.data = data
	}
	copy(me.data[w.Offset:end], d)
	return w.Size, OK
}

func (me *MutableDataFile) Flush() Status {
	return OK
}

func (me *MutableDataFile) Release() {

}

func (me *MutableDataFile) getAttr() *Attr {
	a := &Attr{}
	CopyFileInfo(&me.FileInfo, a)
	a.Size = uint64(len(me.data))

	return a
}

func (me *MutableDataFile) GetAttr() (*Attr, Status) {
	me.GetAttrCalled = true
	return me.getAttr(), OK
}

func (me *MutableDataFile) Fsync(*FsyncIn) (code Status) {
	return OK
}

func (me *MutableDataFile) Utimens(atimeNs uint64, mtimeNs uint64) Status {
	me.FileInfo.Atime_ns = int64(atimeNs)
	me.FileInfo.Mtime_ns = int64(mtimeNs)
	return OK
}

func (me *MutableDataFile) Truncate(size uint64) Status {
	me.data = me.data[:size]
	return OK
}

func (me *MutableDataFile) Chown(uid uint32, gid uint32) Status {
	me.FileInfo.Uid = int(uid)
	me.FileInfo.Gid = int(uid)
	return OK
}

func (me *MutableDataFile) Chmod(perms uint32) Status {
	me.FileInfo.Mode = (me.FileInfo.Mode &^ 07777) | perms
	return OK
}

////////////////

type FSetAttrFs struct {
	DefaultFileSystem
	file *MutableDataFile
}

func (me *FSetAttrFs) GetXAttr(name string, attr string) ([]byte, Status) {
	return nil, syscall.ENODATA
}

func (me *FSetAttrFs) GetAttr(name string) (*Attr, Status) {
	if name == "" {
		return &Attr{Mode: S_IFDIR | 0700}, OK
	}
	if name == "file" && me.file != nil {
		a := me.file.getAttr()
		a.Mode |= S_IFREG
		return a, OK
	}
	return nil, ENOENT
}

func (me *FSetAttrFs) Open(name string, flags uint32) (File, Status) {
	if name == "file" {
		return me.file, OK
	}
	return nil, ENOENT
}

func (me *FSetAttrFs) Create(name string, flags uint32, mode uint32) (File, Status) {
	if name == "file" {
		f := NewFile()
		me.file = f
		me.file.FileInfo.Mode = mode
		return f, OK
	}
	return nil, ENOENT
}

func NewFile() *MutableDataFile {
	return &MutableDataFile{}
}

func TestFSetAttr(t *testing.T) {
	fs := &FSetAttrFs{}

	c := NewFileSystemConnector(fs, nil)
	state := NewMountState(c)
	dir := MakeTempDir()

	state.Mount(dir)
	state.Debug = true
	defer state.Unmount()

	go state.Loop(false)

	fn := dir + "/file"
	f, err := os.OpenFile(fn, os.O_CREATE|os.O_WRONLY, 0755)
	CheckSuccess(err)
	defer f.Close()

	_, err = f.WriteString("hello")
	CheckSuccess(err)

	fmt.Println("Ftruncate")
	code := syscall.Ftruncate(f.Fd(), 3)
	if code != 0 {
		t.Error("truncate retval", os.NewSyscallError("Ftruncate", code))
	}
	if len(fs.file.data) != 3 {
		t.Error("truncate")
	}

	if state.KernelSettings().Flags&CAP_FILE_OPS == 0 {
		log.Println("Mount does not support file operations")
		m, _ := json.Marshal(state.KernelSettings())
		log.Println("Kernel settings: ", string(m))
		return
	}

	_, err = os.Lstat(fn)
	CheckSuccess(err)

	if !fs.file.GetAttrCalled {
		t.Error("Should have called File.GetAttr")
	}

	err = os.Chmod(fn, 024)
	CheckSuccess(err)
	if fs.file.FileInfo.Mode&07777 != 024 {
		t.Error("chmod")
	}

	err = os.Chtimes(fn, 100, 101)
	CheckSuccess(err)
	if fs.file.FileInfo.Atime_ns != 100 || fs.file.FileInfo.Atime_ns != 101 {
		t.Error("Utimens")
	}

	// TODO - test chown if run as root.
}
<commit_msg>Add DefaultFile to MutableDataFile in FSetAttrFs<commit_after>package fuse

import (
	"fmt"
	"log"
	"json"
	"os"
	"syscall"
	"testing"
)

type MutableDataFile struct {
	DefaultFile

	data []byte
	os.FileInfo
	GetAttrCalled bool
}

func (me *MutableDataFile) Read(r *ReadIn, bp *BufferPool) ([]byte, Status) {
	return me.data[r.Offset : r.Offset+uint64(r.Size)], OK
}

func (me *MutableDataFile) Write(w *WriteIn, d []byte) (uint32, Status) {
	end := uint64(w.Size) + w.Offset
	if int(end) > len(me.data) {
		data := make([]byte, len(me.data), end)
		copy(data, me.data)
		me.data = data
	}
	copy(me.data[w.Offset:end], d)
	return w.Size, OK
}

func (me *MutableDataFile) Flush() Status {
	return OK
}

func (me *MutableDataFile) Release() {

}

func (me *MutableDataFile) getAttr() *Attr {
	a := &Attr{}
	CopyFileInfo(&me.FileInfo, a)
	a.Size = uint64(len(me.data))

	return a
}

func (me *MutableDataFile) GetAttr() (*Attr, Status) {
	me.GetAttrCalled = true
	return me.getAttr(), OK
}

func (me *MutableDataFile) Fsync(*FsyncIn) (code Status) {
	return OK
}

func (me *MutableDataFile) Utimens(atimeNs uint64, mtimeNs uint64) Status {
	me.FileInfo.Atime_ns = int64(atimeNs)
	me.FileInfo.Mtime_ns = int64(mtimeNs)
	return OK
}

func (me *MutableDataFile) Truncate(size uint64) Status {
	me.data = me.data[:size]
	return OK
}

func (me *MutableDataFile) Chown(uid uint32, gid uint32) Status {
	me.FileInfo.Uid = int(uid)
	me.FileInfo.Gid = int(uid)
	return OK
}

func (me *MutableDataFile) Chmod(perms uint32) Status {
	me.FileInfo.Mode = (me.FileInfo.Mode &^ 07777) | perms
	return OK
}

////////////////

type FSetAttrFs struct {
	DefaultFileSystem
	file *MutableDataFile
}

func (me *FSetAttrFs) GetXAttr(name string, attr string) ([]byte, Status) {
	return nil, syscall.ENODATA
}

func (me *FSetAttrFs) GetAttr(name string) (*Attr, Status) {
	if name == "" {
		return &Attr{Mode: S_IFDIR | 0700}, OK
	}
	if name == "file" && me.file != nil {
		a := me.file.getAttr()
		a.Mode |= S_IFREG
		return a, OK
	}
	return nil, ENOENT
}

func (me *FSetAttrFs) Open(name string, flags uint32) (File, Status) {
	if name == "file" {
		return me.file, OK
	}
	return nil, ENOENT
}

func (me *FSetAttrFs) Create(name string, flags uint32, mode uint32) (File, Status) {
	if name == "file" {
		f := NewFile()
		me.file = f
		me.file.FileInfo.Mode = mode
		return f, OK
	}
	return nil, ENOENT
}

func NewFile() *MutableDataFile {
	return &MutableDataFile{}
}

func TestFSetAttr(t *testing.T) {
	fs := &FSetAttrFs{}

	c := NewFileSystemConnector(fs, nil)
	state := NewMountState(c)
	dir := MakeTempDir()

	state.Mount(dir)
	state.Debug = true
	defer state.Unmount()

	go state.Loop(false)

	fn := dir + "/file"
	f, err := os.OpenFile(fn, os.O_CREATE|os.O_WRONLY, 0755)
	CheckSuccess(err)
	defer f.Close()

	_, err = f.WriteString("hello")
	CheckSuccess(err)

	fmt.Println("Ftruncate")
	code := syscall.Ftruncate(f.Fd(), 3)
	if code != 0 {
		t.Error("truncate retval", os.NewSyscallError("Ftruncate", code))
	}
	if len(fs.file.data) != 3 {
		t.Error("truncate")
	}

	if state.KernelSettings().Flags&CAP_FILE_OPS == 0 {
		log.Println("Mount does not support file operations")
		m, _ := json.Marshal(state.KernelSettings())
		log.Println("Kernel settings: ", string(m))
		return
	}

	_, err = os.Lstat(fn)
	CheckSuccess(err)

	if !fs.file.GetAttrCalled {
		t.Error("Should have called File.GetAttr")
	}

	err = os.Chmod(fn, 024)
	CheckSuccess(err)
	if fs.file.FileInfo.Mode&07777 != 024 {
		t.Error("chmod")
	}

	err = os.Chtimes(fn, 100, 101)
	CheckSuccess(err)
	if fs.file.FileInfo.Atime_ns != 100 || fs.file.FileInfo.Atime_ns != 101 {
		t.Error("Utimens")
	}

	// TODO - test chown if run as root.
}
<|endoftext|>


<commit_before>package apigee

import (
	"fmt"
	"log"
	"strings"

	"github.com/gofrs/uuid"
	"github.com/hashicorp/terraform/helper/schema"
	"github.com/zambien/go-apigee-edge"
)

func resourceProduct() *schema.Resource {
	return &schema.Resource{
		Create: resourceProductCreate,
		Read:   resourceProductRead,
		Update: resourceProductUpdate,
		Delete: resourceProductDelete,
		Importer: &schema.ResourceImporter{
			State: resourceProductImport,
		},

		Schema: map[string]*schema.Schema{
			"name": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"display_name": {
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"approval_type": {
				Type:     schema.TypeString,
				Required: true,
			},
			"attributes": {
				Type:     schema.TypeMap,
				Optional: true,
			},
			"description": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"api_resources": {
				Type:     schema.TypeList,
				Optional: true,
				Elem: &schema.Schema{
					Type:     schema.TypeString,
					Required: true,
				},
			},
			"proxies": {
				Type:     schema.TypeList,
				Optional: true,
				MaxItems: 100,
				Computed: true,
				ForceNew: true,
				Elem: &schema.Schema{
					Type:     schema.TypeString,
					Required: true,
				},
			},
			"quota": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"quota_interval": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"quota_time_unit": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"scopes": {
				Type:     schema.TypeList,
				Optional: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
			},
			"environments": {
				Type:     schema.TypeList,
				Optional: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
			},
		},
	}
}

func resourceProductCreate(d *schema.ResourceData, meta interface{}) error {

	log.Print("[DEBUG] resourceProductCreate START")

	client := meta.(*apigee.EdgeClient)

	u1, _ := uuid.NewV4()
	d.SetId(u1.String())

	ProductData, err := setProductData(d)
	if err != nil {
		log.Printf("[ERROR] resourceProductCreate error in setProductData: %s", err.Error())
		return fmt.Errorf("[ERROR] resourceProductCreate error in setProductData: %s", err.Error())
	}

	_, _, e := client.Products.Create(ProductData)
	if e != nil {
		log.Printf("[ERROR] resourceProductCreate error in product creation: %s", e.Error())
		return fmt.Errorf("[ERROR] resourceProductCreate error in product creation: %s", e.Error())
	}

	return resourceProductRead(d, meta)
}

func resourceProductImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
	log.Print("[DEBUG] resourceProductImport START")

	client := meta.(*apigee.EdgeClient)
	productData, _, err := client.Products.Get(d.Id())
	if err != nil {
		return []*schema.ResourceData{}, fmt.Errorf("[DEBUG] resourceProductImport. Error getting product: %v", err)
	}
	d.Set("name", d.Id())
	apiResources := flattenStringList(productData.ApiResources)
	proxies := flattenStringList(productData.Proxies)
	scopes := flattenStringList(productData.Scopes)
	environments := flattenStringList(productData.Environments)
	if productData.DisplayName == "" {
		d.Set("display_name", productData.Name)
	} else {
		d.Set("display_name", productData.DisplayName)
	}
	d.Set("display_name", productData.DisplayName)
	d.Set("description", productData.Description)
	d.Set("approval_type", productData.ApprovalType)
	d.Set("attributes", productData.Attributes)
	d.Set("apiResource", apiResources)
	d.Set("proxies", proxies)
	d.Set("quota", productData.Quota)
	d.Set("quota_interval", productData.QuotaInterval)
	d.Set("quota_time_unit", productData.QuotaTimeUnit)
	d.Set("environments", environments)
	d.Set("scopes", scopes)

	return []*schema.ResourceData{d}, nil
}

func resourceProductRead(d *schema.ResourceData, meta interface{}) error {

	log.Print("[DEBUG] resourceProductRead START")
	client := meta.(*apigee.EdgeClient)

	ProductData, _, err := client.Products.Get(d.Get("name").(string))
	if err != nil {
		log.Printf("[ERROR] resourceProductRead error getting products: %s", err.Error())
		if strings.Contains(err.Error(), "404 ") {
			log.Printf("[DEBUG] resourceProductRead 404 encountered.  Removing state for product: %#v", d.Get("name").(string))
			d.SetId("")
			return nil
		} else {
			log.Printf("[ERROR] resourceProductRead error error getting products: %s", err.Error())
			return fmt.Errorf("[ERROR] resourceProductRead error getting products: %s", err.Error())
		}
	}

	apiResources := flattenStringList(ProductData.ApiResources)
	proxies := flattenStringList(ProductData.Proxies)
	scopes := flattenStringList(ProductData.Scopes)
	environments := flattenStringList(ProductData.Environments)

	d.Set("name", ProductData.Name)

	if ProductData.DisplayName == "" {
		d.Set("display_name", ProductData.Name)
	} else {
		d.Set("display_name", ProductData.DisplayName)
	}
	d.Set("display_name", ProductData.DisplayName)
	d.Set("description", ProductData.Description)
	d.Set("approval_type", ProductData.ApprovalType)
	d.Set("attributes", ProductData.Attributes)
	d.Set("apiResource", apiResources)
	d.Set("proxies", proxies)
	d.Set("quota", ProductData.Quota)
	d.Set("quota_interval", ProductData.QuotaInterval)
	d.Set("quota_time_unit", ProductData.QuotaTimeUnit)
	d.Set("environments", environments)
	d.Set("scopes", scopes)

	return nil
}

func resourceProductUpdate(d *schema.ResourceData, meta interface{}) error {

	log.Print("[DEBUG] resourceProductUpdate START")

	client := meta.(*apigee.EdgeClient)

	ProductData, err := setProductData(d)
	if err != nil {
		log.Printf("[ERROR] resourceProductUpdate error in setProductData: %s", err.Error())
		return fmt.Errorf("[ERROR] resourceProductUpdate error in setProductData: %s", err.Error())
	}

	_, _, e := client.Products.Update(ProductData)
	if e != nil {
		log.Printf("[ERROR] resourceProductUpdate error in product update: %s", e.Error())
		return fmt.Errorf("[ERROR] resourceProductUpdate error in product update: %s", e.Error())
	}

	return resourceProductRead(d, meta)
}

func resourceProductDelete(d *schema.ResourceData, meta interface{}) error {

	log.Print("[DEBUG] resourceProductDelete START")

	client := meta.(*apigee.EdgeClient)

	_, err := client.Products.Delete(d.Get("name").(string))
	if err != nil {
		log.Printf("[ERROR] resourceProductDelete error in product delete: %s", err.Error())
		return fmt.Errorf("[ERROR] resourceProductDelete error in product delete: %s", err.Error())
	}

	return nil
}

func setProductData(d *schema.ResourceData) (apigee.Product, error) {

	log.Print("[DEBUG] setProductData START")

	if d.Get("display_name") == "" {
		d.Set("display_name", d.Get("name"))
	}

	apiResources := []string{""}
	if d.Get("api_resources") != nil {
		apiResources = getStringList("api_resources", d)
	}

	proxies := []string{""}
	if d.Get("proxies") != nil {
		proxies = getStringList("proxies", d)
	}

	scopes := []string{""}
	if d.Get("scopes") != nil {
		scopes = getStringList("scopes", d)
	}

	attributes := []apigee.Attribute{}
	if d.Get("attributes") != nil {
		attributes = attributesFromMap(d.Get("attributes").(map[string]interface{}))
	}

	environments := []string{""}
	if d.Get("environments") != nil {
		environments = getStringList("environments", d)
	}

	Product := apigee.Product{
		Name:          d.Get("name").(string),
		DisplayName:   d.Get("display_name").(string),
		ApprovalType:  d.Get("approval_type").(string),
		Attributes:    attributes,
		Description:   d.Get("description").(string),
		ApiResources:  apiResources,
		Proxies:       proxies,
		Quota:         d.Get("quota").(string),
		QuotaInterval: d.Get("quota_interval").(string),
		QuotaTimeUnit: d.Get("quota_time_unit").(string),
		Scopes:        scopes,
		Environments:  environments,
	}

	return Product, nil
}
<commit_msg>PENG-9527: no need to make proxies required<commit_after>package apigee

import (
	"fmt"
	"log"
	"strings"

	"github.com/gofrs/uuid"
	"github.com/hashicorp/terraform/helper/schema"
	"github.com/zambien/go-apigee-edge"
)

func resourceProduct() *schema.Resource {
	return &schema.Resource{
		Create: resourceProductCreate,
		Read:   resourceProductRead,
		Update: resourceProductUpdate,
		Delete: resourceProductDelete,
		Importer: &schema.ResourceImporter{
			State: resourceProductImport,
		},

		Schema: map[string]*schema.Schema{
			"name": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"display_name": {
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"approval_type": {
				Type:     schema.TypeString,
				Required: true,
			},
			"attributes": {
				Type:     schema.TypeMap,
				Optional: true,
			},
			"description": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"api_resources": {
				Type:     schema.TypeList,
				Optional: true,
				Elem: &schema.Schema{
					Type: schema.TypeString,
				},
			},
			"proxies": {
				Type:     schema.TypeList,
				Optional: true,
				Elem: &schema.Schema{
					Type: schema.TypeString,
				},
			},
			"quota": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"quota_interval": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"quota_time_unit": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"scopes": {
				Type:     schema.TypeList,
				Optional: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
			},
			"environments": {
				Type:     schema.TypeList,
				Optional: true,
				Elem:     &schema.Schema{Type: schema.TypeString},
			},
		},
	}
}

func resourceProductCreate(d *schema.ResourceData, meta interface{}) error {

	log.Print("[DEBUG] resourceProductCreate START")

	client := meta.(*apigee.EdgeClient)

	u1, _ := uuid.NewV4()
	d.SetId(u1.String())

	ProductData, err := setProductData(d)
	if err != nil {
		log.Printf("[ERROR] resourceProductCreate error in setProductData: %s", err.Error())
		return fmt.Errorf("[ERROR] resourceProductCreate error in setProductData: %s", err.Error())
	}

	_, _, e := client.Products.Create(ProductData)
	if e != nil {
		log.Printf("[ERROR] resourceProductCreate error in product creation: %s", e.Error())
		return fmt.Errorf("[ERROR] resourceProductCreate error in product creation: %s", e.Error())
	}

	return resourceProductRead(d, meta)
}

func resourceProductImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
	log.Print("[DEBUG] resourceProductImport START")

	client := meta.(*apigee.EdgeClient)
	productData, _, err := client.Products.Get(d.Id())
	if err != nil {
		return []*schema.ResourceData{}, fmt.Errorf("[DEBUG] resourceProductImport. Error getting product: %v", err)
	}
	d.Set("name", d.Id())
	apiResources := flattenStringList(productData.ApiResources)
	proxies := flattenStringList(productData.Proxies)
	scopes := flattenStringList(productData.Scopes)
	environments := flattenStringList(productData.Environments)
	if productData.DisplayName == "" {
		d.Set("display_name", productData.Name)
	} else {
		d.Set("display_name", productData.DisplayName)
	}
	d.Set("display_name", productData.DisplayName)
	d.Set("description", productData.Description)
	d.Set("approval_type", productData.ApprovalType)
	d.Set("attributes", productData.Attributes)
	d.Set("apiResource", apiResources)
	d.Set("proxies", proxies)
	d.Set("quota", productData.Quota)
	d.Set("quota_interval", productData.QuotaInterval)
	d.Set("quota_time_unit", productData.QuotaTimeUnit)
	d.Set("environments", environments)
	d.Set("scopes", scopes)

	return []*schema.ResourceData{d}, nil
}

func resourceProductRead(d *schema.ResourceData, meta interface{}) error {

	log.Print("[DEBUG] resourceProductRead START")
	client := meta.(*apigee.EdgeClient)

	ProductData, _, err := client.Products.Get(d.Get("name").(string))
	if err != nil {
		log.Printf("[ERROR] resourceProductRead error getting products: %s", err.Error())
		if strings.Contains(err.Error(), "404 ") {
			log.Printf("[DEBUG] resourceProductRead 404 encountered.  Removing state for product: %#v", d.Get("name").(string))
			d.SetId("")
			return nil
		} else {
			log.Printf("[ERROR] resourceProductRead error error getting products: %s", err.Error())
			return fmt.Errorf("[ERROR] resourceProductRead error getting products: %s", err.Error())
		}
	}

	apiResources := flattenStringList(ProductData.ApiResources)
	proxies := flattenStringList(ProductData.Proxies)
	scopes := flattenStringList(ProductData.Scopes)
	environments := flattenStringList(ProductData.Environments)

	d.Set("name", ProductData.Name)

	if ProductData.DisplayName == "" {
		d.Set("display_name", ProductData.Name)
	} else {
		d.Set("display_name", ProductData.DisplayName)
	}
	d.Set("display_name", ProductData.DisplayName)
	d.Set("description", ProductData.Description)
	d.Set("approval_type", ProductData.ApprovalType)
	d.Set("attributes", ProductData.Attributes)
	d.Set("apiResource", apiResources)
	d.Set("proxies", proxies)
	d.Set("quota", ProductData.Quota)
	d.Set("quota_interval", ProductData.QuotaInterval)
	d.Set("quota_time_unit", ProductData.QuotaTimeUnit)
	d.Set("environments", environments)
	d.Set("scopes", scopes)

	return nil
}

func resourceProductUpdate(d *schema.ResourceData, meta interface{}) error {

	log.Print("[DEBUG] resourceProductUpdate START")

	client := meta.(*apigee.EdgeClient)

	ProductData, err := setProductData(d)
	if err != nil {
		log.Printf("[ERROR] resourceProductUpdate error in setProductData: %s", err.Error())
		return fmt.Errorf("[ERROR] resourceProductUpdate error in setProductData: %s", err.Error())
	}

	_, _, e := client.Products.Update(ProductData)
	if e != nil {
		log.Printf("[ERROR] resourceProductUpdate error in product update: %s", e.Error())
		return fmt.Errorf("[ERROR] resourceProductUpdate error in product update: %s", e.Error())
	}

	return resourceProductRead(d, meta)
}

func resourceProductDelete(d *schema.ResourceData, meta interface{}) error {

	log.Print("[DEBUG] resourceProductDelete START")

	client := meta.(*apigee.EdgeClient)

	_, err := client.Products.Delete(d.Get("name").(string))
	if err != nil {
		log.Printf("[ERROR] resourceProductDelete error in product delete: %s", err.Error())
		return fmt.Errorf("[ERROR] resourceProductDelete error in product delete: %s", err.Error())
	}

	return nil
}

func setProductData(d *schema.ResourceData) (apigee.Product, error) {

	log.Print("[DEBUG] setProductData START")

	if d.Get("display_name") == "" {
		d.Set("display_name", d.Get("name"))
	}

	apiResources := []string{""}
	if d.Get("api_resources") != nil {
		apiResources = getStringList("api_resources", d)
	}

	proxies := []string{""}
	if d.Get("proxies") != nil {
		proxies = getStringList("proxies", d)
	}

	scopes := []string{""}
	if d.Get("scopes") != nil {
		scopes = getStringList("scopes", d)
	}

	attributes := []apigee.Attribute{}
	if d.Get("attributes") != nil {
		attributes = attributesFromMap(d.Get("attributes").(map[string]interface{}))
	}

	environments := []string{""}
	if d.Get("environments") != nil {
		environments = getStringList("environments", d)
	}

	Product := apigee.Product{
		Name:          d.Get("name").(string),
		DisplayName:   d.Get("display_name").(string),
		ApprovalType:  d.Get("approval_type").(string),
		Attributes:    attributes,
		Description:   d.Get("description").(string),
		ApiResources:  apiResources,
		Proxies:       proxies,
		Quota:         d.Get("quota").(string),
		QuotaInterval: d.Get("quota_interval").(string),
		QuotaTimeUnit: d.Get("quota_time_unit").(string),
		Scopes:        scopes,
		Environments:  environments,
	}

	return Product, nil
}
<|endoftext|>


<commit_before><commit_msg>replace watchers per machine with a global one<commit_after><|endoftext|>